Functions

Functions are first-class citizens in Zith. The language supports multiple function kinds, implicit returns, and closures.

Return Type & Implicit Returns

fn add(a: i32, b: i32): i32 { a + b }    // explicit type, implicit return
fn add(a: i32, b: i32)      { a + b }    // inferred return type

// Bound-check: if ok, return normally; if not, propagate as null
fn first<T>(slice: []T): ?T {
    slice[0]?
}

ℹ Note: The compiler cannot infer a union return type without an explicit union type hint.

Function Kinds

Kind Description
fn Standard runtime function.
const fn Resolved at compile time. Must be called inside a const block.
async fn Coroutine (default async). Can yield.
async<Generator> fn Explicit generator — yields a typed sequence.
flow fn Enables marker/dock/jump control flow. Can be noreturn.
raw fn Always unchecked — bypasses safety in both debug and release.

⚠ Function kinds are orthogonal and cannot be mixed.

Macro Calls vs Function Calls

Macro calls use the @ prefix — @println, @log, @serialize.
Function calls use bare names — console.write, process, save.

async fn & yield

// Default coroutine
async fn fetch(url: string): Response! {
    yield;
    get_response()!
}

// Explicit generator
async<Generator> fn range(start: i32, end: i32): ?i32 {
    for (i in start..end) {
         yield i;
         i += 1;
    }
}

for (v in range(0, 10)) { @println(v); }

Closures

A closure is a pack with callable semantics. Capture modes:

let addBase = |base| (n: i32): i32 { base + n };
let f1 = |view x| (n: i32) { x + n };
let f2 = |x| (n: i32) { x += n; };

static vs dyn

By default a closure generates an inline (static) call. The dyn qualifier makes it an indirect call via function pointer.

let closure: dyn = |base| { ... };

// Extract / inject the pack at compile time
let pack = @pack closure;
@pack closure = pack;

Dynamic Levels

Level Syntax Semantics
Full static let f = \|x\| x + 1; Zero overhead.
Semi-dynamic let f: dyn = \|x\| x + 1; Function pointer; capture signature statically known.
Dynamic pack, static fn let f: \|opaque\| = \|x\| x + 1; Capture pack opaque, no type info inside.
Full dynamic let f: dyn \|opaque\| = \|x\| x + 1; Both function and capture opaque; full fat pointer.

See also: Spec: Functions


Next: Control Flow