Sage-Code Laboratory
index<--

Eve Functions

A function is a relation between input and output values. A function makes a computation to establish the output. Eve functions are objects of type Function. Eve enable functional programming paradigm (FP).

Page bookmarks:



Function declaration

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.

Pattern:

#function declaration
function name(parameters) => (@result:TypeName):
  ** executable block
  ...
return;

Notes:

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.

Pattern:


#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.

Formal parameters

Function 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.

Example:

# 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;

Closures

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.

Why closures?

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

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.

Example

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 signature

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 & states

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.

Example:


# 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;
Warning: Observe we have used "set" to create the initial value for a global variable. Then we alter it using "let" inside the process. The new value is used by the function to cut several decimals after round(). This is not exactly a side-effect but is an influence, that is not accepted in functional programming.

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