4. Functions & Lambdas {#4-functions-lambdas}

July 5, 2026 ยท View on GitHub

+++ title = "4. Functions & Lambdas" weight = 4 +++

4. Functions & Lambdas {#4-functions-lambdas}

Functions

fn add(a: int, b: int) -> int {
    return a + b;
}

// Named arguments supported in calls
add(a: 10, b: 20);

{% alert(type="note") %} Named arguments must strictly follow the defined parameter order. add(b: 20, a: 10) is invalid. {% end %}

Const Arguments

Function arguments can be marked as const to enforce read-only semantics. This is a type qualifier, not a manifest constant.

fn print_val(v: const int) {
    // v = 10; // Error: Cannot assign to const variable
    println "{v}";
}

Default Arguments

Functions can define default values for trailing arguments. These can be literals, expressions, or valid Zen C code (like struct constructors).

// Simple default value
fn increment(val: int, amount: int = 1) -> int {
    return val + amount;
}

// Expression default value (evaluated at call site)
fn offset(val: int, pad: int = 10 * 2) -> int {
    return val + pad;
}

// Struct default value
struct Config { debug: bool; }
fn init(cfg: Config = Config { debug: true }) {
    if cfg.debug { println "Debug Mode"; }
}

fn main() {
    increment(10);      // 11
    offset(5);          // 25
    init();             // Prints "Debug Mode"
}

Lambdas (Closures)

Anonymous functions that can capture their environment.

let factor = 2;
let doubler = x -> x * factor;  // Arrow syntax
let full = fn(x: int) -> int { return x * factor; }; // Block syntax

// Capture by Reference (Block Syntax)
let val = 10;
let modify = fn[&]() { val += 1; }; 
modify(); // val is now 11

// Capture by Reference (Arrow Syntax)
let modify_arrow = [&] x -> val += x;
modify_arrow(5); // val is now 16

// Capture by Reference (Arrow Syntax with Multiple Arguments)
let sum_into = [&] (a, b) -> val += (a + b);
sum_into(2, 2); // val is now 20

// Capture by Value (Default)
let original = 100;
let implicit = x -> original + x;       // Implicit capture by value (no brackets)
let explicit = [=] x -> original + x;   // Explicit capture by value
// let fail = x -> original += x;       // Error: cannot assign to captured value

Raw Function Pointers

Zen C supports raw C function pointers using the fn* syntax. This allows seamless interop with C libraries that expect function pointers without closure overhead.

// Function taking a raw function pointer
fn set_callback(cb: fn*(int)) {
    cb(42);
}

// Function returning a raw function pointer
fn get_callback() -> fn*(int) {
    return my_handler;
}

// Pointers to function pointers are supported (fn**)
let pptr: fn**(int) = &ptr;

Variadic Functions

Functions can accept a variable number of arguments using ... and the va_list type.

fn log(lvl: int, fmt: char*, ...) {
    let ap: va_list;
    va_start(ap, fmt);
    vprintf(fmt, ap); // Use C stdio
    va_end(ap);
}