A closure is an unnamed function value created with =>.
double := value => value * 2
add := (a: int, b: int) => a + b
constant := () => 42Closures can be passed to functions, stored in bindings, returned when the inferred type permits it, and used by collection operations.
Parameters
A one-parameter closure may omit parentheses.
square := value => value * valueUse parentheses for zero or multiple parameters.
nothing := () => 0
sum := (left, right) => left + rightParameter types may be inferred from the expected function type.
fn apply(value: int, operation: fn(int) -> int): int {
return operation(value)
}
answer := apply(21, value => value * 2)Write annotations when the context does not determine the parameter types.
compare := (left: int, right: int) => left < rightClosure parameters do not support declaration defaults or variadic forms.
Bodies
The expression after => is the closure’s result.
absolute := value => if value < 0 { -value } else { value }A brace block supports statements and early returns from the closure itself.
normalize := (value: int) => {
if value < 0 {
return -value
}
value
}The semantic checker unifies the body with the expected function result. A fallible closure can propagate errors when its expected function type carries the corresponding error contract.
Trailing calls
A final closure argument can be written after the ordinary argument list.
positive := values.filter { value => value > 0 }
retried := retry(3) { () => perform_work() }The braces belong to the trailing closure argument, not to the call’s result. This form is useful for callbacks with multi-line bodies.
items.for_each {
item => {
println(item)
}
}Captures
A closure may use names from its surrounding lexical scope.
factor := 3
scale := value => value * factorThe compiler determines the capture set. There is no capture list in source. Immutable scalar captures behave as captured values; reference-shaped values retain the storage needed by the closure.
prefix := "item"
format := value => "$prefix: $value"Captured values remain alive for as long as an escaping closure needs them. Programmers do not manually allocate or release a closure environment.
Mutable captures
A closure can observe and update a surrounding mutable binding when the checker can establish one shared mutable place.
fn make_counter(): fn() -> int {
mut count := 0
return () => {
count += 1
count
}
}Closures from the same capture group that share a mutable local observe the
same updates. The compiler moves escaping mutable captures into managed
environment storage; this is not a user-visible Cell type.
Mutation remains subject to normal exclusivity and lifetime rules. Capturing a name does not make an immutable binding mutable.
Escape
A closure escapes when it can outlive the activation that created it—for example, when it is returned, stored in a longer-lived value, or passed across an unknown call boundary.
fn make_adder(amount: int): fn(int) -> int {
return value => value + amount
}The compiler allocates a typed closure environment for an escaping closure. Non-escaping closures may be inlined or lowered to direct helper calls. These choices do not change source semantics.
Suspension
A closure can cross a suspension point only when all reachable captured state has a resumable representation. The compiler keeps escaping environments in arena-managed storage and includes the closure’s effects in call analysis.
Passing a closure to a suspend-capable function is conservatively treated as an escaping boundary unless analysis proves otherwise. This is why a closure that is immediately invoked may optimize differently from one stored in a task or stream callback.
Function compatibility
Named functions and closures can both satisfy compatible function types.
fn twice(value: int): int => value * 2
named: fn(int) -> int = twice
anonymous: fn(int) -> int = value => value * 2Parameter types, success types, error types, and declared effects all participate in compatibility. A closure is not converted through an untyped callback representation.
Collection use
Lists, slices, maps, sets, strings, and iterators expose higher-order methods. Common examples include mapping, filtering, searching, folding, and iteration.
even := values.filter { value => value % 2 == 0 }
doubled := values.map { value => value * 2 }Exact method names and mutability requirements are documented in the Standard Library.