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.

No comments:

Post a Comment