Thursday, October 16, 2014

What's new: ELENA 1.9.17 - generic methods

In this post I will discuss generic methods introduced in 1.9.7 version.

Let's start with two basic things. Any message in the language consist of a verb (predefined action : get, set, insert, add, ...), a signature (user defined) and a number of parameters

For example in the following expression

aBinary insert &index:0 &literal:"0"

a message

insert&index&literal[2] 

is used. insert is a verb, index&literal is a signature (consisting of two subjects: index and literal) and 2 is a number of parameters.

Or in this message call

anS length

get&length[0] is used

And secondly the message can be created dynamically at run time combining a signature symbol and a generic message (a message without a signature)

anS ~ %length get.

These two principles are used in the generic method implementation. A generic method may responds to the message with the same verb and the parameter number but with any signature. Original signature is presaved and can be used inside the method.

Let's consider a simple example. Suppose we have the class containing coordinates

#class Point
{
   #field theX.
   #field theY.

   #constructor new &x:anX &y:anY
   [
       theX := anX.
       theY := anY.  
   ]

   #method x = theX.

   #method y = theY.
      
   #method set &x:anX
   [
       theX := anX.
   ]
      
   #method set &y:anY
   [
       theY := anY.
   ]

   #method clone = Point new &x:theX &y:theY.

   #method literal = "Point(x:" + theX literal + ", y:"
                          + theY literal + ")".
}

Let's create a variable which can contain a point

#class PointVariable
{
   #field thePoint.

   #constructor new &point:aPoint
   [
      thePoint := aPoint.
   ] 

   #method value = thePoint.
}

And let's define the operations with the point coordinates. We will use generic methods

#class PointVariable                                                                
{  
   ...

   #method(generic) append : aValue
   [
      thePoint~$subject set:(thePoint~$subject get + aValue).
   ]
       
   #method(generic) reduce : aValue
   [
      thePoint~$subject set:(thePoint~$subject get - aValue).
   ]
       
   #method(generic) multiplyBy : aValue
   [
      thePoint~$subject set:(thePoint~$subject get * aValue).
   ]
       
   #method(generic) divideInto : aValue
   [
      thePoint~$subject set:(thePoint~$subject get / aValue).
   ]
}

How it works? Let's consider the simple use case

   #var aVar := PointVariable new 
                   &point:(Point new &x:1 &y:1).

   aVar append &x:2.
   
   console writeLine:(aVar value literal).

When append&x[1] message is sent to an instance of PointVariable, a class dispatcher tries to resolve the message directly, if no match was found it will call the generic method - append, a built-in variable contains our original signature - %x.

thePoint~ %x get 

is similar to

thePoint x

and

thePoint ~%x set:aValue

similar to

thePoint set &x:aValue

Note that our example will work with a Point class containing arbitrary number of coordinates (one, two, three and so on)

This principle is used in system'dynamic'DynamicStruct class.

   #var r1 := system'dynamic'DynamicStruct new.
   #var r2 := system'dynamic'DynamicStruct new.

   r1 set &Price:20.5r set &Count:3.
   r2 set &Name:"John" set &LastName:"Smith".
   r1 set &Supplier:r2.

1 comment:

  1. This comment has been removed by a blog administrator.

    ReplyDelete