Functions

Why This Matters

Functions are your primary API boundary for data ownership, effects, and behavior. If your function signatures are clear, the rest of the codebase stays predictable.

Basic Shape

function add(a: Integer, b: Integer): Integer {
    return a + b;
}

Ownership Modes In Parameters

  • owned (default): function takes ownership
  • borrow: read-only borrow
  • borrow mut: borrow-mut mode (caller-side exclusivity contract)

Current compiler behavior notes:

  • calling borrow mut parameters requires mutable caller binding
  • inside callee, parameter can be read and reassigned (println(x), x += 1)
  • caller-visible mutation propagation is type-dependent in current behavior
  • if you need explicit and predictable caller-visible in-place mutation semantics, use &mut T
import std.io.*;

function consume(owned s: String): None {
    println("consumed={s}");
    return None;
}

function readName(borrow s: String): None {
    println("read={s}");
    return None;
}

mut, &, &mut At Function Boundaries

Quick rule for beginners:

  • parameter x: T receives owned value semantics
  • parameter x: &T receives read-only reference
  • parameter x: &mut T receives mutable reference (in-place update path)
import std.io.*;

function show(x: &Integer): None {
    println("x={*x}");
    return None;
}

function bump(x: &mut Integer): None {
    *x += 1;
    return None;
}

function main(): None {
    mut n: Integer = 10;
    show(&n);
    bump(&mut n);
    println("after={n}");
    return None;
}

Use this as default API design:

  • read-only helper -> &T
  • mutating helper -> &mut T
  • ownership transfer intentionally required -> owned T

Return Style

Use explicit return for clarity, especially in beginner-facing code and non-trivial branches.

main() Entry Constraints

Current compiler rules for main():

  • no parameters
  • no generic parameters
  • cannot be async
  • cannot be extern
  • cannot be variadic
  • return type must be None or Integer

Higher-Order Functions

Functions are first-class values and can be passed around.

function apply(x: Integer, f: (Integer) -> Integer): Integer {
    return f(x);
}

Runnable Example

import std.io.*;

function square(x: Integer): Integer {
    return x * x;
}

function apply(x: Integer, f: (Integer) -> Integer): Integer {
    return f(x);
}

function main(): None {
    println("result={apply(7, square)}");
    return None;
}

Common Mistakes

  • oversized functions mixing validation, logic, and side effects
  • unclear ownership in signatures when borrowing is intended
  • using borrow mut where &mut T parameter would make mutation path clearer
  • returning sentinel values instead of explicit Option/Result style
Built and maintained by TheRemyyy. Arden is open source under Apache 2.0 and published at theremyyy.dev.