Functions are declared with keyword: "function". A function can be public or private. You can define public functions in modules and private functions in other scripts.
#function declaration
function name(parameters) => (@result:TypeName):
** executable block
...
return;
Execute function Function is executed in expressions using the name of the function followed by semicolon, and list of arguments in round brackets (). Brackets are mandatory even if there are no arguments.
Function arguments The arguments can be pass by position or by name. The names of arguments is useful when when a function has optional parameters.
#demo function execution
driver function_ex:
** declare functions
function name1(param :Type,...) => (@result:Type):
let result := expression;
return;
function name2(param := value,...) => (@result:Type):
let result := expression;
return;
process:
** execute using argument list
new result1 := name1(arguments);
** execute using parameter names with pair operator (:)
new result2 := name2(param:arg,....);
return;
Note: Argument value can be an expression or a variable that translate to a value of expected type. For clarity of application we encourage creation of local variables for arguments.
There is a difference between the parameter and the argument. The parameter is a local variable in the function scope while arguments are values assigned to these parameters in a function call. Arguments can be literals, constants, variables or expressions.
# demo function with parameters
driver function_params:
function sum(a, b: Integer) => (@result:Integer):
let result := (a + b);
return;
process:
print sum(1,2); -- 3
print sum(2,4); -- 6
return;
A closure is a function created by another function. The closure has read/write access to context variables. The context is the scope of parent function, that is called high order function. The context is replicated and bound to every closure instance.
Closures are enabled in Eve to offer a convenient way to create generators. These are functions that can return a different result for every call. A closure is a light weight object. Like objects closures are dynamicly created at runtime using "new" keyword.
driver test_closure:
# declare a high order function
function generator(start:Integer) => (@closure:Function):
new current := start;
** define a closure using let
let closure := Function() => (@result:Integer):
let current += 1;
let result := current;
return;
return;
process:
** instantiate two closures
new index1 := generator(0);
new index2 := generator(10);
** interlace the two calls
print index1(), index1(); -- 1, 2
print index2(), index2(); -- 11, 12
print index1(), index1(); -- 3, 4
return;
Notes:
Lambda expressions are simpler functions that respect arithmetic rules. These can receive several arguments and can produce one result. A lambda expression is created with keywords "set" or "new". Lambda expressions do not have local variables or states and do not have side effects.
Lambda expressions can be declared in global region but also in the process region. You can define more than one global regions in a script. Lambda expressions are associated to a variable, sometimes can be associated to a member in a collection.
In next example we define two lambda expressions.
global
set sum = (p1, p2 :Integer) => (p1 + p2):Integer;
set sqr = (p1, p2 :Integer) => (p1 * p2):Integer;
Lambda expression can be declared as data type using keyword "class". Later, this type can be used to enforce a specific signature for a call-back argument, variable or collection element.
# define a lambda signature
driver lam_sig
** Sefine callback signature
class BinEx = (p1, p2 :Integer):Integer <: Lambda;
** Use lambda signature as parameter type
routine useLambda(test :BinEx):
print test(); -- execute callback
return;
** Execute a process with lambda argument
process:
** use anonymous lambda argument
call useLambda((x, y) => (x + y));
return;
Note: Lambda expressions can be created by a function or method as a result. Lambda expressions can't modify values of variable declared in the parent scope.
Lambda expressions can read global or parent scope states, but can not modify them. However, if external values change after expression is defined, the result may be influenced by the new values.
# verify shared state influence
driver shared_states:
** define global settings
set decimals = 2: Integer;
global
set trunc = (x:Real) => floor(x* 10^decimal)/10^decimal;
process:
new x := 10/3;
** call the lambda expression
print trunc(x) -- expected 3.33
** overwrite the initial settings
let decimals := 4;
print trunc(x) -- expected 3.3333
return;
Disclaim: We acknowledge, Eve is not a pure functional language. This behavior is present in many other modern languages. Eve is not different. We seek for a balance between the theory and pragmatism.
Read next: Classes