Monday, October 4, 2010

Method Calling tutorial

From the first programming languages procedures (or functions) play very important role. Introduction of object-oriented paradigm increase this role even more. So it is not possible to program a language without learning the way how it handles procedures. This tutorial will show that this is not so trivial in ELENA.

When ELENA programming language was designed I made an important decision - to limit the number of method parameters to just one (concept of the limited object interface). It was done to make the object interface (or protocol if you wish) as much polymorphic as possible (selecting one set of parameters over others implies the way how it will be used; anyone who tried to add another method with the same name in the C++ base class knows that it could be a painful process).

So how String.Insert(index, value) method could be implemented if you cannot provide another parameter? There are general several ways how to do this. First way would be to introduce a special class (in this case std'basic'LiteralIndexer) which will be a mediator (agent) between string and the piece of code where the substring is inserted. So in ELENA it will look like this anStr@index insert:value ( @ operator will return an indexer pointing to specified position). One good thing with this that it is a code pattern. So every time we would like to insert something into an indexable collection we will use this way. But sometimes we have to set several peer parameters (like x and y coordinates). Of course we could set them independently but if it is important to set them simultaneously the second way could be used - aControl location'set: { location'x = 20. location'y = 20. }. The good thing is that location'set is a polymorphic method ( no change in the method interface have to be made if we decided to use three-dimensional or polar coordinates). The bad thing that the expression is quite big. So a special simplified version could be used - aControl location'set &x:20 &y:20. If the method requires no parameters a special nil symbol is passed to it - aControl control'open ( <=> aControl control'open:nil).

Being dynamic ELENA does not allow to implement a method overload a la C++. Instead the polymorphic nature of method parameter can be used. For example std'patterns'IndexEnum::pattern'run could use two set of parameters - ctrl'IndexEnum &for:anArray &action: => [ .. ] and ctrl'IndexEnum &from:anArray@2 &action: => [ ... ]. std'basic' LiteralIndexer :: literalinfo'create method is another example. If we would like to copy a substring from the specific position till the end we could omit parameter at all - aStr@2 literalinfo'create. If we wish to provide a substring length - aStr@2 literalinfo'create &length:10. If we are interested in copying a substring from one indexer till another - aStr@0 literalinfo'create &till:(aStr@1 literal'seek:" ").

Note that in all examples above a single method body was used. If we need a specific implementation for different parameters multi-dispatching routine should be used. In ELENA multi-dispatching is similar to traditional polymorphism with the only difference that executing code depends on a parameter rather than the object. For example if the method parameter is an integer - an integer operation should be executed. If it is a real number - real one and so on. e.g. - aWriter write:2 write:2.3r write:"literal" should correctly write to the stream an integer, a real and a literal values.

How could we achieve this on the language with the late binding (no compiler magic like in static languages)? Several ways could be used. One of them would be to ask the parameter to do the operation itself (after all who knows how to handle the object better than the object itself?). The problem with these approach (which is used in ELENA as well) that the object should know all possible operation with it. In ELENA a method naming limitation makes the things even worse (It is not possible to call the method copyToInteger, lessThenInteger as it would be discussed later). So as an alternative the concept of the subject was introduced.

The subject could be considered as a declarative type (a unique token). For example std'basic'Literal, std'basic'String and gui'control'Edit are all implement literal subject (note that they do not share common code with each other except a generic super class). The subject should be declared before use. It is inherited but the class may support only one subject (it is done with a special compiler hint - #hint[subj:literal] before a class or symbol declaration).

Before continuing let's consider another topic where a subject is used - method naming. As opposed to many main stream languages ELENA limits the way how a method can be named. Any public message may consist of subject and verb (generic messages have only verb part of the name). The number of verbs are limited and well defined (e.g. and, get, set, read, write, start, run and so on). The subject is defined by the user and could represent an actor or type (bool'and, int'add, literal'read, indexer'get; see above). The subject may represent the process as well (pattern'run, process'start and so on).

So let's return to multi-dispatching mechanism. If our class (stream writer for example) handles a particular subject (integer, real or literal values) the method name should contain the appropriate subject (int'write, real'write and literal'write). This approach allows the object to notify any potential user of expecting parameter type (note that in a language with late binding like ELENA the mismatch type errors can be found only in run-time) or returning value (literal'get) or expected operation - (process'stop). As a plus this helps us with multi-dispatching as well. If we mark our class as dispatchable (#hint[dispatchable]) when the expression aWriter write:2 write:2.3r write:"literal" will actually invoke int'write, real'write and literal'write respectively.

No comments:

Post a Comment