LEVIATHAN v962456e · 962456eee1

Standard Library · expr

class Expr<F>

A lambda together with a walkable description of its body.

since 0.1.0-alpha.1linuxwindowswasm

Overview

Where a parameter or variable has type expr::Expr<F>, a lambda literal produces both parts: fn is the ordinary closure, and tree, binds and siteId describe the same body as data. The tree mirrors the checked body of the lambda, so a consumer such as a query builder can translate it instead of running it. The body must be a single expression, and only a fixed subset of expressions can be reified; anything else is a compile error that names the construct.

The example evaluates a reified lambda two ways: by calling fn, and by walking tree with the values in binds.

Examples

Evaluating a reified lambda

class User { int age; new User(int a) { age = a; } }

int asInt(string | int | float | bool | None v) {
    match (v) {
        int => { return v; }
        else => { return 0; }
    }
}

// A small walker for the nodes this lambda produces: it evaluates
// Field, Lit, Bind and comparison Bin nodes over a User.
bool evalNode(expr::Node n, User u, Array<string | int | float | bool | None> binds) {
    int value = evalInt(n, u, binds);
    return value != 0;
}

int evalInt(expr::Node n, User u, Array<string | int | float | bool | None> binds) {
    match (n) {
        expr::Field => { return u.age; }
        expr::Lit   => { return asInt(n.v); }
        expr::Bind  => { return asInt(binds[n.slot]); }
        expr::Bin   => {
            expr::Bin b = n;
            int x = evalInt(b.l, u, binds);
            int y = evalInt(b.r, u, binds);
            if (b.op == ">=") { if (x >= y) return 1; return 0; }
            throw RuntimeException("unhandled operator " + b.op);
        }
        else => { throw RuntimeException("unhandled node"); }
    }
}

int minAge = 18;
expr::Expr<(User) => bool> adult = (u) => u.age >= minAge;
console.writeln("captured values: ${adult.binds.length()}");
console.writeln("closure: ${adult.fn(User(20))} ${adult.fn(User(15))}");
console.writeln("tree: ${evalNode(adult.tree, User(20), adult.binds)} ${evalNode(adult.tree, User(15), adult.binds)}");
captured values: 1
closure: true false
tree: true false

Constructors

new

new(F f, expr::Node t, Array<string | int | float | bool | None> b, int s)

Build an Expr from its four parts.

The compiler calls this for you when a lambda literal reifies; calling it yourself is useful for hand-built trees.

Parameters

f
The closure.
t
The root of the node tree.
b
The captured values.
s
The site identifier.

Examples

Array<string | int | float | bool | None> binds = [];
expr::Expr<(int) => int> e = expr::Expr((x) => x + 1, expr::Bin("+", expr::Field(["x"]), expr::Lit(1)), binds, 7);
console.writeln(e.fn(4));
console.writeln(e.siteId);
5
7

Fields

binds

Array<string | int | float | bool | None> binds

The captured values the tree refers to by Bind slot, each read once when the Expr was built, in order of first use.

fn

F fn

The lambda as an ordinary closure; call it like any function.

siteId

int siteId

A number that identifies the lambda in the source program, unique within one compilation.

tree

The root of the node tree that mirrors the lambda body.

See also

  • Node — The base class of every node in the reification tree.