Reference
First-Class Functions and Closures
Look up syntax, contracts, layouts, algorithms, and exact behavior.
OJaml supports first-class functions through heap-allocated closures and WebAssembly function tables. A top-level function can be called directly when statically known, or wrapped as a closure when passed as a value. Anonymous and local functions become pending lambdas with captured variables recorded from free-variable analysis. A local recursive function stores its own closure pointer in the captured environment when the function body refers to its name. The function-table ABI is generated from the program, so closure calls are not capped at a small fixed arity; returned and staged closures use the same generated call path.
closure pointer p
p + 0 table index
p + 4 captured value 0
p + 8 captured value 1
...
indirect call:
call_indirect(type fn_n)
env = p
arg_1 ... arg_n
table_index = load(p + 0)let make_adder x =
fun y -> x + y
let main =
let add10 = make_adder 10 in
add10 32A closure stores a table index followed by captured values. Indirect calls load the function table index from the closure pointer and pass the closure pointer as an environment parameter. Lambda bodies recover captured values by loading from fixed offsets in that environment.
let main =
let xs = List.cons 3 (List.cons 2 (List.cons 1 (List.empty ()))) in
List.fold_left (fun acc x -> acc + x) 0 xsThis representation keeps direct calls efficient while supporting higher-order collection functions such as map, filter, exists, for_all, iter, and fold_left. The compiler emits the arity-specific WebAssembly function types required by the current program, and the standard library calls callbacks through call_indirect with the matching type.
- Top-level functions receive closure wrappers when they are used as values.
- Pending lambdas are emitted after top-level declarations, with stable function-table indices.
- Indirect-call arities are generated from the current program, so closure calls scale with the highest first-class or staged function arity the source actually uses.
- Captured locals and captured captures are both loaded into the new closure environment.