Closures

A lambda is an anonymous function, written as a parameter list followed by => and an expression body. Lambdas are commonly passed to higher-order functions such as map and filter:

void main() {
    var numbers = List([0, 1, 2, 3, 4]);

    var even = numbers.filter(n => n % 2 == 0).toList();
    println(even); // prints [0, 2, 4]

    var doubled = even.map(n => n * 2).toList();
    println(doubled); // prints [0, 4, 8]
}

When a lambda is passed directly to a function, its parameter types are inferred from the expected function pointer type. When there is no expected type, for example when storing a lambda in a variable, the parameter types must be written explicitly:

int apply(int(int) f, int x) {
    return f(x);
}

void main() {
    var triple = (int n) => n * 3;
    println(triple(14)); // prints 42
    println(apply(n => n + 1, 41)); // prints 42, parameter type inferred
}

A lambda body can also be a block with statements. In that case the => before the brace is optional:

void main() {
    var f = (int x, int y) {
        var sum = x + y;
        return sum * 2;
    };
    println(f(10, 11)); // prints 42
}

Captures

Stored lambdas capture variables from the enclosing scope by copy:

void main() {
    int threshold = 100;
    var isBig = (int n) => n >= threshold;
    println(isBig(42)); // prints false
    println(isBig(101)); // prints true
}

Capturing lambdas cannot convert to function pointer types; they can be stored in variables and called directly. They can also be passed to generic higher-order functions such as filter and map, which are generic over the callback type so each call site is specialized for its lambda with no dynamic allocation for the closure:

void main() {
    var numbers = List([0, 1, 2, 3, 4]);
    int threshold = 2;
    println(numbers.filter(n => n >= threshold)); // prints [2, 3, 4]
}

Constants

A lambda passed to a method on a constant receives constant elements, so its borrow parameters bind const: reading through them works, but writing is rejected. This also applies through call chains, which conservatively includes chains through copying calls such as toList(); bind such a result to a variable first to work with it mutably:

void main() {
    const arr = [1, 2, 3];
    println(arr.map(n => *n + 1)); // prints [2, 3, 4]
    // arr.map(n => { *n = 99; return *n; }); // error: cannot assign to constant
}