Eve Lambda Expressions

An expression has a value. A lambda expression is an expression whose value is a function: it makes a small function in one line, where a value is expected. Use it for a short calculation that does not need a name, a state or a block of statements.

What is a lambda expression

Lambda is a notation, not a type: a lambda expression creates a function in a single statement from one or more expressions. Parentheses are mandatory, a list of expressions creates several results, and the type goes after the parentheses: ():Type or ():(Type, Type). A lambda assigned to an identifier that is not defined is just a pointer; it can be sent to a parameter that receives a pointer @param.

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 the global scope but also in a process. 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.


  set sum = (p1, p2 :Integer) => (p1 + p2):Integer;
  set mul = (p1, p2 :Integer) => (p1 * p2):Integer;

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 is

  ** define global settings
  set decimals = 2: Integer;
  set trunc = (x:Real) => floor(x* 10^decimals)/10^decimals;

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

Disclaimer: 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.

Where a lambda can stand

PlaceExample
assigned to a nameset sum = (a, b: Integer) => (a + b):Integer;
an argument of a call (a callback)useLambda((x, y) => (x + y));
a member of a collectionnew ops := ((a, b) => (a + b), (a, b) => (a * b));
the result of a functionfunction adder(n: Integer) => (@f: Function) is let f := (x) => (x + n); return;

Rules

  • A lambda holds expressions only: no statements, no local variables and no state;
  • A lambda does not change anything. It may read values of its creator, and it sees the new value when that changes (see "Lambda & states");
  • For a name, a state or several statements, use a function, and for a state kept between calls, a closure (Closures);
  • The type of a lambda is a function type: see Callbacks.
TODO: replace the lambda examples that use the old assignment set sum = … ; with the declaration order of D-079 and check each one against spec/syntax when the spec is written.
TODO: write what a lambda does when the types of its parameters are omitted: inferred from the type of the parameter that receives it ((x, y) => (x + y) in the callback example), and when the inference fails.
TODO: decide the collection operations that take a lambda: map, filter, sort(by), reduce (Q-026). Then add a section with one example for each.

Read next: Callbacks