Tuesday, September 25, 2012

ELENA 2012: Interaction with Objects - External roles

Being a dynamic language ELENA allows to override the object methods in run-time. This feature is called a dynamic mutation (both permanent and temporal). It is done with a help of a generic handler. The generic handler is a special type of the class which handles all incoming messages (physically it is a special VMT with the single entry) and may modify the incoming message or its target. Let's consider a group handler. The group handler is a an array of objects which may be considered as a single one, i.e. they share a common instance reference (SELF variable). Actually we override SELF variable dynamically. As a result SELF variable may no longer always point to the current class instance. That's why in ELENA there are two instance reference: SELF (a "virtual" reference to the current object) and $SELF (a "static" reference to the current object). In normal case both SELF and $SELF are equal. But if the object becomes a part of the group, SELF points to the group and $SELF to the object itself. This feature is actively used in external roles.

An external role is a set of additional methods which can extend existing classes without inheriting them (roughly similar to C# extension methods). They are actively used in LIB27 to implement additional functionality which is not considered to be essential to the class (the concept of "poor" interface). For example std'patterns'ForEach is used to enumerate a collection:

#var aList := List += 1 += 2 += 3.
__group(ForEach, aList enumerator) run: 
 anItem = ('program'output << anItem << "%n")

where we dynamically extends an enumerator with ForEach functionality - ability to execute a code for each member of the collection.

Note that we actually do not need to make this mutation permanent. After all in different part of the code the different functionality may be required. So to follow "just in time functionality" concept we could rewrite this code using a wrap group:

__wrap(ForEach, aList enumerator) run: #symbol PrintingLn.

Let's examine this code. __wrap is a group handler which combines the interface (ForEach role) with a content (a collection enumerator) in such a way that incoming messages are redirected to the interface while SELF variable points to the content. Actually we create (just in time when we need it) a new temporal object (so it can be called a temporal mutation) which hides the content object behind the interface (wrap the content in the interface). Instead of nested symbol we could reuse already existing symbol (ext'patterns'PrintingLn).

The code above could be simplified by using extension operator:

aList enumerator ~ForEach  run:#symbol PrintingLn.

Lety's use a factory-symbol NewEnumerator to make it more generic:

NewEnumerator::aList ~ForEach  run:#symbol PrintingLn.

Note that this code will work for an array as well:

aList := (1,2,3).
NewEnumerator::aList ~ForEach  run:#symbol PrintingLn.

External roles can be used both stand-alone or with an argument list:

'program'output write &numeric:9000 &radix:2 &:eintformatter << "%n".

This code is equivalent to the following one:

'program'output write:
   __group(eintformatter, { numeric = 9000. radix = 2. }).

Note that in this could we actually parameterize EIntFormatter, i.e. dynamically extend it with "just-in-time" defined parameters.

Thursday, September 20, 2012

ELENA 2012: Interaction with Objects

As in the most of dynamic object-oriented languages the main way to interact with objects in ELENA is sending a message. Unlike others there are only limited set of possible message names (verbs, e.g. add, set, get, run, seek and so on). Though it is possible to provide the message namespace (subject). The message may contain a parameter (otherwise nil symbol is passed). When several parameters should be passed an argument list can be declared. If the object want 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 or the program is stopped. Some generic messages (verbs without subjects) have alternative names (operators) which have a different parsing order.

The simple code to send a generic method looks like this:

'program'output 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:

'program'output write "2 + 2 =" write:(2 add:2).

We could use operators to have the shorter code:

'program'output << "2+2=" << 2 + 2.

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

Generic messages is used if we apply the operation to the whole object, if we need to work with a part of the object (roughly similar to a class property) we have to use qualified message.

'program'output << """Hello world"" contains " 
   << "Hello world" std'dictionary'length'get << " characters".

This code can be simplified. If the subject module is already defined at the source code beginning, its namespace may be skipped. Secondly in "get" messages (if the parameter is not provided) the verb part can be omitted:

#define std'dictionary'*.


'program'output << """Hello world"" contains " 
   << "Hello world" length << " characters".

In all examples above we use only a single parameter. But what to do if we need to pass several ones? The simple solution would be to create a special objects which will aggregate these parameters - argument list:

aDictionary append: { dictionary_key = "foo". content = "bar". }.

Note that the target object should interact with the parameter to get required data, hence the parameter becomes an active part of the interaction (this feature can be used in method multiple dispatching for example).

We could use a stack allocated argument list and simplify the code:

aDictionary append &dictionary_key:"foo" &content:"bar"

To be continued...

Wednesday, September 12, 2012

New Release ELENA 1.7.16: Language Reloaded

For the past several months I was busy with refactoring the language. Now the work is practically done (except several GUI samples) and in the next several posts I will describe the latest language specification.

The most significant changes were made for the language messaging system. Old style dispatching (which was applicable only for the small number of cases), argument signature were discarded, syntax was changed and several new features were added.

Another point of changes is a dynamic programming. It was completely overwritten.

Actions symbols were discarded. The symbol reference (both nested and explicit) should be used.

Stack allocated objects were introduced for nested symbols and argument lists.

Changes were made for control flow statements (new #until loop statement).

The code was moved to a new library: LIB27.