Callables

Callables are function values. They are Thread-Stack-managed: referencing a function allocates its frame on the heap and stores a pointer to the function in front of it.

The fn Type

// no params, no return
fn[() -> void]

// two params, one return
fn[i32, i32 -> i32]

// multiple returns: no parens
fn[i32, i32 -> i32, i32]

Special-case notations:

// params only, no return (-> void omitted)
fn[i32]

// neither params nor return
fn[]

Function References

def greet():
    print("Hello, World!\n");

def main():
    // '::' is the reference operator
    fn[() -> void] g = ::greet;
    g();
  • Core module and extern functions cannot be referenced (::print is an error).
  • Callables are freed/overwritten on reassignment; copying a callable clones its frame.

Callables as Values

Pass, return, and mutate callables like any value:

def apply_op(fn[i32, i32 -> i32] op, i32 x, i32 y) -> i32:
    return op(x, y);

def get_sub() -> fn[i32, i32 -> i32]:
    return ::sub;

// callables pass by reference
def set_sub(mut fn[i32, i32 -> i32] op):
    op = ::sub;

def main():
    fn[i32, i32 -> i32] op = get_sub();
    i32 res = apply_op(op, 20, 10);
    set_sub(op);
    res = apply_op(op, res, 5);

Persistent Locals

persistent keeps local state across calls; per callable instance, not static/shared:

def counter() -> i32:
    persistent i32 c = 0;
    // '++' not usable in expression contexts yet
    i32 current = c;
    c++;
    return current;

def main():
    fn[() -> i32] c1 = ::counter;
    fn[() -> i32] c2 = ::counter;
    c1(); // 0
    c1(); // 1
    c2(); // 0
  • Cannot combine persistent with const.
  • Direct calls (counter()) reset the state each invocation; only callable instances persist.

Use cases: counters, rate-limiters (with Core.time), state machines, accumulators.

Error Sets in Callables

// possible errors in the fn type
fn[bool {ErrAssert}] f = ::may_fail;

f(true) catch err:
    switch err:
        ErrAssert(e): print($"ErrAssert = {e}\n");
        anyerror(e):  print($"anyerror = {e}\n");

An unspecified error set expands to {anyerror}:

// This expands to the type fn[bool -> void {anyerror}]
fn[bool] f = ::may_fail;

so any function reference can be stored regardless of the errors it may throw.