Thursday, November 28, 2013

ELENA 2: Messaging

There was major overhaul in the way how messaging is implemented in ELENA so in this tutorial I will once again cover this topic.

Unlike strong typed languages the process of invoking the method is called sending a message because the actual method resolving happens at run-time and is implemented by scanning through the target’s VMT. In ELENA it could be even more complex because the actual message can be calculated at run-time as well. It could be redirected to another object with the help of redirect method as well. So message sending is more powerful operation but it comes with a cost. In general it costs more than the conventional method calling. Using redirect handler adds another overhead. So it is not wonder that ELENA is slower than static binding languages. A lot of efforts were made in the new version to reduce this overhaul.

The major change was to allow several parameters to be passed with the message. Though it sounds simple in reality it required major changes in the language implementation, especially parameter dispatch routine (currently it still can be done only for single parameter). The order of parameters in the stack had to be reversed; redirect algorithm was modified and so on. Another change was an introduction of open argument list

Now, let’s look at messaging in details

As in the most of dynamic object-oriented languages the main way to interact with objects in ELENA is sending a message. Unlike others the message name is structured and consists of a verb, a signature and a parameter counter. The verb defines a message action, for example read or write some data. There are only limited set of possible verbs (e.g. eval[uate], add, set, get, run, seek and so on). In general the signature is user defined and describes the message parameters. It can be used to define some custom action as well (e.g. writeLine, which in fact is eval&writeLine(1)). If the signature is not provided the message is considered to be generic and can be qualified (for example by dispatching).

If the object wants to handle the message it has to contain the method with the same name. If no method mapping was found the flow is considered to be broken and the control goes to the next alternative flow (exception handler) or the program is stopped.

The simple code to send a message looks like this:

console write:"Hello World".

Note: "write" is a generic message; a literal constant is a parameter.

Several messages can be send in one statement, the parameter itself may be result of object interactions:

console write "2 + 2 =" write:(2 add:2).

We could use operators to have the shorter code:

console << "2+2=" << 2 + 2.

Note: In most cases "<<" is a synonym to "write" and "+" to "add".

Several parameters can be passed in the message as well:

control foreach:(1,2,3) &do:printingLn.

Ampersand is used to indicate that the signature has several arguments (subjects). The actual message name is eval&foreach&do(2).

The generic message can have several parameters as well:

consoleEx writeLine:”a+b=”:(a + b).

To be continued...

Monday, November 4, 2013

ELENA 2.0 Tutorial: Accumulator factory

In this tutorial we will implement another Rosetta code sample: Accumulator.

Our task is to implement the function which will return another function accumulating passed parameters.

Let’s start with declaring our accumulator function. Note that ELENA is an object-oriented language and any function is in fact an object (or in our case – a symbol).

#symbol EFunction =
{
   eval : x
   [
       ^ self append:x.
   ]
}.

Or we could simplify the code using a generic closure (function symbol):

#symbol EFunction =
 (:x) [ self append:x ].

Note that our code cannot be used stand alone, because we do not declare “append” method. It is in fact an object extension (that’s why it starts with E).

So another function should be declared – it will combine the passed variable (supporting “append” method) with our accumulating function – creating a wrap group object:

#symbol Accumulator = (:aVariable)
    [ Wrap(EFunction, aVariable) ].

To understand how this code works let’s recall what is a group object. In short group object overwrites SELF variable (but not $SELF one). Various group objects do it differently. Wrap one replaces SELF in the extender (the first one) with the content (the second one). To put it another way it wraps the extender around the content: when we call append method, Wrap group object redirects the message to its first member replacing SELF with the second one.

The use case will be the following:

#var x := Accumulator : (Integer new:1).
x:5.

Let’s optimize the code a bit, to allow our symbol accepting constant values as well

#symbol Accumulator =(:anInitialValue)
    [ Wrap(EFunction, Variable new:anInitialValue) ].

So we could drop the variable creating:

#var x := Accumulator : 1.
x:5.

The full code is below:

#define system.
#define system'dynamic.
 
#symbol EFunction = 
    (:x) [ self append:x ].
 
#symbol Accumulator = (:anInitialValue)
    [ Wrap(EFunction, Variable new:anInitialValue) ].
 
#symbol Program =
[
    #var x := Accumulator:1.
 
    x:5.
 
    #var y := Accumulator:3.
 
    console write:(x:2.3r).
].