Category:Elena: Difference between revisions

From Rosetta Code
Content added Content deleted
No edit summary
Line 10: Line 10:
{{language programming paradigm|Object-oriented}}{{language programming paradigm|dynamic}}
{{language programming paradigm|Object-oriented}}{{language programming paradigm|dynamic}}


ELENA is a general-purpose, object-oriented, polymorphic language with late binding. It features message dispatching/manipulation, dynamic object mutation, a script engine / interpreter and mix-ins.
== Overview ==


== Namespaces ==
ELENA is a general-purpose, object-oriented, polymorphic language with late binding. It features message dispatching / manipulation, dynamic object mutation, a script engine / interpreter and group object support.


Any program or library consists of modules containing classes and symbols.
Any ELENA program or library consists of modules ( files with .NL extension ) containing classes and symbols. Every member of the module is referred by its fully qualified name which consists of namespace and a proper name separated by an apostrophe. The namespace itself may contain sub elements separated by apostrophes.


All source files (files with .L extension) located in the same folder are compiled into the corresponding module. A project file ( a file with .PRJ extension ) defines the root namespace and the output type (stand-alone executable, VM executable or a library). The project may produce several modules if it contains the files located in sub folders (the new module namespace consists of the root one and the folder relative path is split by apostrophes).
The main way to interact with objects in ELENA is sending a message. 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. The signature is user defined and describes the message parameters. If the signature is not provided the message is considered to be generic and can be qualified (by dispatching).


== Messaging ==
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. It is possible to declare generic handler which will be called for all incoming messages.


The main way to interact with objects in ELENA is sending a message. The message
== Namespaces ==
name is structured and consists of a operation, parameter signature and a parameter counter.


operation [parameter_attribute]* [parameter_counter]
Any ELENA program or library consists of modules (files with .NL extension) containing
classes and symbols. Every class or symbol may be referred by its namespace
(or to put it other way around a symbol namespace is a path to the symbol module).


e.g.
All source files (files with .L extension) located in the same folder are compiled into
the corresponding module. A project file (a file with .PRJ extension) defines the root
namespace and the output type (stand-alone executable, VM executable or a library).
The project may produce several modules if it contains the files located in sub folders
(the new module namespace consists of the root one plus the folder relative path)


writeLine[2] - generic one
== Messaging ==
writeLine&LiteralValue&LiteralValue[2] - strong one


If the signature is omitted the message may be multi-dispatched.
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.
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.
Line 43: Line 38:
console write:"Hello World".
console write:"Hello World".


Note: "write" is a generic message; a literal constant is a parameter.
Note: *write* is a generic message; a literal constant is a parameter. A dot is a statement terminator.


If the parameter is an expression itself it should be enclose in the round brackets:
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).
console write:("Hello " add:"World").


In this case a colon may be dropped:
We could use operators to have the shorter code:


console << "2+2=" << 2 + 2.
console write("Hello " add:"World").


Several messages can be send in one statement separated by semicolon:
Note: In most cases "<<" is a synonym to "write" and "+" to "add".


console write:"2 + 2 ="; write(2 add:2).
Several parameters can be passed in the message as well:


We could use operators to have the shorter code:
control foreach: (1,2,3) do:printingLn.


console write(2 + 2).
The generic message can have several parameters as well:


Note: In most cases *"+"* is a synonym to *add*.
consoleEx writeLine:”a+b=”: (a + b).


Several qualified parameters can be passed in the message:

console write:"Hello World" paddingLeft:10 with:$32

The message name is *write&paddingLeft&with[2]*.

The generic message can have several parameters as well:


console writeLine("a+b=",a + b).
== Classes, Roles and Symbols ==


== Classes, Symbols, Nested classes and Closures ==
ELENA is an object-oriented language, so to create a program we have to declare new classes.


ELENA is an object-oriented language. To create a program we have to declare new classes and symbols.
A class encapsulates data (fields) with code (methods) to access it. In most cases it is not possible to get a direct access to the class content (it makes sense for dynamic languages when in the most cases code is generic and can be applied for different "types"). Usually the fields refer to another classes and so on until we reach "primitive" ones which content are considered as raw data (e.g. numeric or literal values).


A class encapsulates data (fields) with code (methods) to access it. In most cases it is not possible
To work with the class we have to create its instance with the help of the special methods - constructors. A constructor is used mostly to initialize the class fields. There are special type of classes which do not have fields and constructors and can be used directly (roles).
to get a direct access to the class content. Usually the field refers to another class and so on
until we reach "primitive" ones which content are considered as raw data (e.g. numeric or literal values).


Classes form the inheritance tree. There is the common super class - system'Object.
Classes form the inheritance tree. There is the common super class - system'Object.
ELENA does not support multiple inheritance, though it is possible to inherit the code using redirect handler (so called "horizontal inheritance"). When the parent is not provided the class inherits directly system'Object (the super class).
ELENA does not support multiple inheritance, though it is possible to inherit the code using a dispatch handler
(mixins / group objects). When the parent is not provided the class inherits directly system'Object
(the super class).

A class instance can be created with the help of the special methods - constructors. A constructor
is used mostly to initialize the class fields. There are special types of classes which do not have constructors
and can be used directly (singletons, nested classes, extensions, closures). A class itself is considered as a stateless object.


class BaseClass
class BaseClass
Line 82: Line 92:
field1 = theField1.
field1 = theField1.
field2 = theField.
field2 = theField2.
}
}
class DerivedClass :: BaseClass
class DerivedClass :: BaseClass
{
{
constructor new field1:aField2 field2:aField2
constructor new field1:object1 field2:object2
[
[
theField1 := aField1.
theField1 := object1.
theField2 := aField2.
theField2 := object2.
]
]
add field1:aField2 field2:aField2
add field1:object1 field2:object2
= MyClass new Field1:(theField1 + aField1) Field2:(theField2 + aField2).
= MyClass new field1(theField1 + object1) field2(theField2 + object1).
}
}
To create a class instance we have to send a message (usually new) to its symbol (a class symbol is declared implicitly for every class and can be used as a normal one)
To create a class instance we have to send a message (usually new) to its class.


var anObject := DerivedClass new field1:1 field2:1. // DerivedClass is a symbol
var anObject := DerivedClass new field1:1 field2:1.


A symbol is a named expression and can be used to declare initialized objects, constants, reusable expressions
Singletons cannot have constructors and their symbols can be used directly
and so on.


class ClassHelper =
const N = 1.
symbol TheClass = DerivedClass new field1:N field2:N.

A static symbol is the named expression which state is preserved. There could be only one instance of static symbol.

static SingletonClass = DerivedClass new field1:0 field2:0.

Nested classes can be declared in the code and used directly.

var ClassHelper =
{
{
sumOf:a:b
sumOf:object1:object2
= a add field1:b field2:a.
= anObject1 add field1:object1 field2:object2.
}
}.
...
...
var aSum := ClassHelper sumOf:1:2.
var aSum := ClassHelper sumOf:anObject1:anObject2.


Closure is a special case of the nested class and consists of the code enclosed into square brackets
In general the symbol is a named expression and can be used to declare initialized objects, constants, reusable expressions and so on.
(with optional parameter declaration)


str forEach:(:ch) [ console write:ch. ].
symbol ZeroClass = DerivedClass new field:0 field:0.


Note: a colon may be dropped:
A static symbol is the class instance which state is preserved. There could be only one instance of static symbol.


str forEach(:ch) [ console write:ch. ].
static SingletonClass = DerivedClass new field:0 field:0.


== Code blocks ==
== Code blocks ==


ELENA code block consists of a sequence of statements. The block is enclosed in square brackets and may contain nested sub code blocks (which in fact are inline action classes). The statement terminator is a dot.
ELENA code block consists of one or more declarations and statements. The block is enclosed in square brackets and may contain nested sub code blocks. The statement terminator is a dot.


control run int:0 int:MAX every(:i)
printAckermann n:n m:m
[
[
control forrange int:0 int:n do: (&int:i)
pi := pi + -1.0r power int:i / (2*i+1) * 4.
[
].

control forrange int:0 int:m do: (&int:j)
console writeLine:pi.
[
console writeLine("Time elapsed in msec:",aDiff milliseconds).
...
console writeLine
].
].
]


When a method should return a result (other than self) return statement is used. It should be the last statement in the block.
When a method should return a result (other than $self) return statement is used. It should be the last statement in the block.


[
[
Line 157: Line 175:
== Conditional branching ==
== Conditional branching ==


ELENA like Smalltalk does not support any special language constructs to implement the conditional branching. Instead special Boolean symbols (system’true and system’false) are used. All conditional operations should return these symbols as a result.
Conditional branching is implemented with a help of special Boolean symbols (system’true and system’false). All conditional operations should return these symbols as a result.


There are three branching methods : if[1] , if[2], else[1]
There are three branching methods : _if[1]_ , _if[2]_, _ifnot[1]_


(m == 0) if:
(m == 0) if:
[
[
n + 1
r append:(n + 1).
]
]
: [
: [
m + n
r append:(m + n).
].
].

Note that code in square brackets are in fact nested action classes ( an action class is a class supporting evaluate message). So this code is can be written in this form:

(m == 0) if:
{
eval
[
^ n + 1.
]
}
: {
eval
[
^ m + n.
]
}.


This expression can be written using special operators
This expression can be written using special operators


(m == 0)
(m == 0)
? [ ^n + 1 ]
? [ r append:(n + 1). ]
! [ ^m + n ].
! [ r append:(m + n). ].

Note: the main difference between using explicit messages and conditional operators is that the compiler may optimize the resulting code in the later case.


We could omit true or else part
We could omit true or else part


(m == 0)
(m == 0)
! [ ^m / n ].
! [ m / n ].


Boolean symbols supports basic logical operations (AND, OR, XOR and NOT), so several conditions can be checked
It is possible to use *if* template code :


if (m == 0)
((aChar >= 48) and:(aChar < 58))
[ n := n + 1. ];
? [
[ n := m + n. ].
theToken += aChar
]
! [
Exception new:"Invalid expression"; raise
]


**if** code template could be used:
In this case the compiler always use optimized code for branching


if ((aChar >= 48) and:(aChar < 58))
Boolean symbols supports basic logical operations (AND, OR, XOR and NOT), so several conditions can be checked
[
theToken append:aChar
];
[
Exception new:"Invalid expression"; raise
].


Note that in this case both condition will be evaluated even if the first one is false. If we want to use short-circuit evaluation, lazy expression should be used:
if (aChar >= 48) and:(aChar < 58)
[
theToken append:aChar.
];
[
Exception new:"Invalid expression"; raise.
]


Note that in this case both condition will be evaluated even if the first one is false If we want to use short-circuit evaluation expression brackets should be used
if ((x >= 0)and:$(array@x != 0))
if ((x >= 0)and:$(array@x != 0))
[
[
Line 225: Line 228:


aBulls =>
aBulls =>
-1 [ consoleEx writeLine:"Not a valid guess.". ^ true ];
-1 [ console writeLine:"Not a valid guess.". ^ true ];
4 [
4 [
consoleEx writeLine:"Congratulations! You have won!".
console writeLine:"Congratulations! You have won!".
^ false
^ false
];
];
Line 233: Line 236:
theAttempt append:1.
theAttempt append:1.
consoleEx writeLine:
console printLine(
"Your Score is " : aBulls
"Your Score is ", aBulls,
: " bulls and " : aCows : " cows".
" bulls and ", aCows, " cows").
^ true
^ true
].
].

== Hello world program ==

To write a simple console application, we have to declare the program main symbol - an object handling _eval[0]_ message. The simplest way is to declare a nested class:

program =
{
eval
[
system'console writeLine:"Hello World"
]
}.

A nested class containing only one _eval_ method can be declared as a closure:

program =
[
system'console writeLine:"Hello World"
].

Finally we may import system namespace:

import system.

program =
[
console writeLine:"Hello World" .
].


== See also ==
== See also ==

Revision as of 06:46, 5 October 2017

Language
Elena
This programming language may be used to instruct a computer to perform a task.
Official website
Execution method: Compiled (bytecode)
Garbage collected: Yes
Type safety: Safe
Type strength: Strong
Type expression: Implicit
Type checking: Dynamic
See Also:
Listed below are all of the tasks on Rosetta Code which have been solved using Elena.


ELENA is a general-purpose, object-oriented, polymorphic language with late binding. It features message dispatching/manipulation, dynamic object mutation, a script engine / interpreter and mix-ins.

Namespaces

Any ELENA program or library consists of modules ( files with .NL extension ) containing classes and symbols. Every member of the module is referred by its fully qualified name which consists of namespace and a proper name separated by an apostrophe. The namespace itself may contain sub elements separated by apostrophes.

All source files (files with .L extension) located in the same folder are compiled into the corresponding module. A project file ( a file with .PRJ extension ) defines the root namespace and the output type (stand-alone executable, VM executable or a library). The project may produce several modules if it contains the files located in sub folders (the new module namespace consists of the root one and the folder relative path is split by apostrophes).

Messaging

The main way to interact with objects in ELENA is sending a message. The message name is structured and consists of a operation, parameter signature and a parameter counter.

  operation [parameter_attribute]* [parameter_counter]

e.g.

  writeLine[2] - generic one
  writeLine&LiteralValue&LiteralValue[2] - strong one

If the signature is omitted the message may be multi-dispatched.

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. A dot is a statement terminator.

If the parameter is an expression itself it should be enclose in the round brackets:

   console write:("Hello " add:"World").

In this case a colon may be dropped:

   console write("Hello " add:"World").

Several messages can be send in one statement separated by semicolon:

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

We could use operators to have the shorter code:

   console write(2 + 2).

Note: In most cases *"+"* is a synonym to *add*.

Several qualified parameters can be passed in the message:

   console write:"Hello World" paddingLeft:10 with:$32

The message name is *write&paddingLeft&with[2]*.

The generic message can have several parameters as well:

   console writeLine("a+b=",a + b).

Classes, Symbols, Nested classes and Closures

ELENA is an object-oriented language. To create a program we have to declare new classes and symbols.

A class encapsulates data (fields) with code (methods) to access it. In most cases it is not possible to get a direct access to the class content. Usually the field refers to another class and so on until we reach "primitive" ones which content are considered as raw data (e.g. numeric or literal values).

Classes form the inheritance tree. There is the common super class - system'Object. ELENA does not support multiple inheritance, though it is possible to inherit the code using a dispatch handler (mixins / group objects). When the parent is not provided the class inherits directly system'Object (the super class).

A class instance can be created with the help of the special methods - constructors. A constructor is used mostly to initialize the class fields. There are special types of classes which do not have constructors and can be used directly (singletons, nested classes, extensions, closures). A class itself is considered as a stateless object.

   class BaseClass
   {
     object theField1.
     object theField2.
     
     field1 = theField1.
   
     field2 = theField2.
   
   }
   
   class DerivedClass :: BaseClass
   {
     constructor new field1:object1 field2:object2
     [  
        theField1 := object1.
        theField2 := object2.
     ]
   
     add field1:object1 field2:object2
        = MyClass new field1(theField1 + object1) field2(theField2 + object1).
   }
   

To create a class instance we have to send a message (usually new) to its class.

   var anObject := DerivedClass new field1:1 field2:1.

A symbol is a named expression and can be used to declare initialized objects, constants, reusable expressions and so on.

   const N = 1. 

   symbol TheClass = DerivedClass new field1:N field2:N.

A static symbol is the named expression which state is preserved. There could be only one instance of static symbol.

   static SingletonClass = DerivedClass new field1:0 field2:0.

Nested classes can be declared in the code and used directly.

   var ClassHelper =
   {
      sumOf:object1:object2
         = anObject1 add field1:object1 field2:object2.
   }.
   
   ...
   
   var aSum := ClassHelper sumOf:anObject1:anObject2.

Closure is a special case of the nested class and consists of the code enclosed into square brackets (with optional parameter declaration)

   str forEach:(:ch) [ console write:ch. ].

Note: a colon may be dropped:

   str forEach(:ch) [ console write:ch. ].

Code blocks

ELENA code block consists of one or more declarations and statements. The block is enclosed in square brackets and may contain nested sub code blocks. The statement terminator is a dot.

   control run int:0 int:MAX every(:i)
   [
       pi := pi + -1.0r power int:i / (2*i+1) * 4.
   ].
   console writeLine:pi.
   console writeLine("Time elapsed in msec:",aDiff milliseconds).

When a method should return a result (other than $self) return statement is used. It should be the last statement in the block.

   [
       ...
   
       ^ aRetVal / anArray length
   ]

If the code block contains only return statement the simplified syntax can be used:

   Number = convertor toReal:theValue.    

It is possible to declare the block variable and assigns the value to it. The variable name must be unique within the code block scope.

   var aRetVal := Integer new:0.


Conditional branching

Conditional branching is implemented with a help of special Boolean symbols (system’true and system’false). All conditional operations should return these symbols as a result.

There are three branching methods : _if[1]_ , _if[2]_, _ifnot[1]_

   (m == 0) if:
   [
      r append:(n + 1).
   ]
   : [
      r append:(m + n).
   ].

This expression can be written using special operators

   (m == 0) 
     ? [ r append:(n + 1). ]
     ! [ r append:(m + n). ].

We could omit true or else part

   (m == 0) 
      ! [ m / n ].

Boolean symbols supports basic logical operations (AND, OR, XOR and NOT), so several conditions can be checked

   ((aChar >= 48) and:(aChar < 58))
   ? [
       theToken += aChar
   ]
   ! [
      Exception new:"Invalid expression"; raise
   ]
    • if** code template could be used:
   if ((aChar >= 48) and:(aChar < 58))
      [
         theToken append:aChar
      ];
      [
        Exception new:"Invalid expression"; raise
      ].

Note that in this case both condition will be evaluated even if the first one is false. If we want to use short-circuit evaluation, lazy expression should be used:

   if ((x >= 0)and:$(array@x != 0))
   [
       ...
   ]

A switch statement can be implemented using => operator

   aBulls =>
        -1 [ console writeLine:"Not a valid guess.". ^ true ];
         4 [ 
                 console writeLine:"Congratulations! You have won!". 
                 ^ false
             ];
           ! [
                theAttempt append:1.
                
                console printLine(
                     "Your Score is ", aBulls,
                     " bulls and ", aCows, " cows").
                
                ^ true
           ].

Hello world program

To write a simple console application, we have to declare the program main symbol - an object handling _eval[0]_ message. The simplest way is to declare a nested class:

   program =
   {
       eval
       [
           system'console writeLine:"Hello World"
       ] 
   }.

A nested class containing only one _eval_ method can be declared as a closure:

   program =
   [
       system'console writeLine:"Hello World"
   ].

Finally we may import system namespace:

   import system.
   program =
   [
       console writeLine:"Hello World" .
   ].

See also

Subcategories

This category has the following 3 subcategories, out of 3 total.

Pages in category "Elena"

The following 200 pages are in this category, out of 240 total.

(previous page) (next page)
(previous page) (next page)