Thursday, January 5, 2012

New Group Types

The latest 1.6.12 release contains several critical changes in the way how the language works with group objects and subjects. In this article I will cover some of these changes.

First of all I made several cosmetic changes: the class extension keywords - #annex and #union were renamed to #join and #outer respectively. Group keywords #group, #union and #cast are no longer supported, built-in classes should be used instead. And a generic handler (#generic) was added.

Detailed discussion we will start with class extensions: #join expression. #join extension can be used both inside the class declaration and as a part of the inline (nested) class expression.

#class MyClass
{
    ...

    #join EMyExtension.
}

If the keyword is followed by an external role (or a stateless class) it is used for "horizontal" inheritance. It is roughly similar to the multiply interface inheritance (note that the class "parents" should not have their own fields).

#class MyIndexer
{
    #field theItem.

    ...

    #join theItem.
}

But the joining expression can be a normal object (e.g class field) as well. In this case it is similar to the group object and is used for run-time extension (permanent dynamic mutation). For example it is used in ELENA indexers to extend the collection item with navigating functionality. Note that in both cases "self" built-in variable refers to the extended class instance (so it is similar to a group object).

'program'output << #join(anObject) { literal = "object:" + anObject literal. }.

In this case the inline class declaration can be used as well.

Now let's go to #outer (join) extension.

#class Variable
{
    #field theValue.

    #method content = theValue.

    #method content'set : aValue
    [
        theValue := aValue.
    ]

    #outer theValue.
}

Outer join extension can be used only inside the class declaration and in most cases is used with objects. The main difference with join extension is that "self" variable is not overridden. This type of extension is used mostly in containers (a dynamic variable).

#class MyClass
{
    ...

    #generic
    [
        'program'output << "My class does not support this method".
        $self fail.
    ]
}

#generic extension is a special method which is called for any unhandled message.

Now let's go to the main topic of this article: group objects. We will start an implicit group object. Before I will continue let me remind you what is ELENA group object: a collection of the objects accessible through the common instance reference (see here).

#var aSecond := aList~EItem @ 1.

A group object is used to dynamically extend the existing object with a new functionality (some kind of mutation). For example in this case we extends the collection with an ability to return the collection member by index. A group can be temporal if the group is immediately followed by a message and the mutator is an external role or a stateless class.

The group can be created explicitly with a help of built-in class - __group.

#var anExtendedList := __group(EItem, aList).

Note that in this case the group is always permanent.

Another way to temporal extend the object is a wrap group (__wrap built-in class)

#var aStr := WideStrValue::__wrap(EInt32Variant, aNumber).

A wrap is a special group which uses the first member of the collection as a wrapper around the second one (content). It means that only the wrapper methods are invoked but "self" variable is assigned to the wrap content. In most cases the wrapper object should be a role or a stateless class. Note that a wrap should have only two members.

Before we will continue with other group types, let me say a few words about subjects. As you may probably know in ELENA language the message consists of message subject (actually a message namespace) and a message verb (built in list of names such as get, set, add, append and so on). A subject should be declared before use. Simultaneously a special symbol (named expression) with the same name is declared. Due to this the program could handle a subject like any other program element (without any kind reflection).

#var aProp1 := aObject subject'get.
#var aProp2 := anObject~subject get.
#var aSubj := subject. #var aProp3 := __wrap(aSubj, anObject) get.

Both these expressions are equivalent but in the third case actually a variable referring to the subject symbol is used.

A special case of the wrap group is a property collection (__prop built-in class)

Compare these two equivalent expressions:

#class egetadapter
{
    #method get = self.
}

...

#var anArray1 := NewArray::{ &array_size:1024 }.

#var anArray2 := NewArray::__prop(array_size, egetadapter, 1024).

The property collection consists of a subject symbol, a wrapper and a content. When a message is sent to the collection the property handler checks its subject and if it is equal to the property subjects the generic message (only the message verb) is sent to the wrapper with "self" variable assigned to the wrap content. As you may see the property collection is similar to the "traditional" class property. Note that in this case no new class is declared and the subject could be dynamic.

And our final group type is a union (__union). Union (similar to outer class extension) could be considered as a collection of dynamic variables.

#var anArray1 := NewArray::{ &array_size:1024 &pattern:integer::0 }.

#var anArray2 := NewArray::__union(__prop(array_size, egetadapter, 1024), __prop(pattern, egetadapter, integer::0)).