Functions in Velo capture the variables of their defining scope. The captured environment lives as long as the function value itself, even after the enclosing function has returned. |
|
makeAdder returns a lambda that adds a captured number. Each call to makeAdder produces a fresh closure with its own n.
|
func makeAdder(int n) func[int] {
func(int x) int {
x + n;
};
};
func[int] add5 = makeAdder(5);
func[int] add10 = makeAdder(10);
add5(3); # 8
add10(3); # 13
|
Closures can capture mutable state. Two counters made from the same factory keep their counts independent. |
func makeCounter() func[int] {
int count = 0;
func() int {
count = count + 1;
count;
};
};
func[int] c1 = makeCounter();
func[int] c2 = makeCounter();
c1(); # 1
c1(); # 2
c2(); # 1 — independent state
c1(); # 3
|
A factory can return multiple closures sharing the same captured variable — a classic recipe for encapsulated state. |
class Pair(func[void] add, func[int] get) {};
func makeAccumulator() Pair {
int total = 0;
new Pair(
func(int v) void { total = total + v; },
func() int { total; }
);
};
Pair acc = makeAccumulator();
acc.add(7);
acc.add(35);
acc.get(); # 42
|
Currying: a function that returns another function lets you bind arguments one at a time. |
func add(int a) func[int] {
func(int b) int {
a + b;
};
};
func[int] addTen = add(10);
addTen(5); # 15
add(3)(4); # 7
|
Closures are ordinary values: store them in fields, arrays, or pass them across function boundaries. Their captured environment is reachable as long as the closure itself is reachable. |
class Button(str label, func[void] onClick) {};
int clicks = 0;
Button b = new Button("OK", func() void {
clicks = clicks + 1;
});
b.onClick();
b.onClick();
# clicks is now 2
|