Skip to content

Functions

Where a function looks up the names it uses is in Variables & Scope.

FormMeaning
fn name(params) { … }A named function, at the top level of a file
fn name(params) -> Type { … }With an annotated return type
private fn name(params) { … }Not exported from a module
fn(params) { … }A function value (closure), anywhere an expression fits

return value; leaves the function. Without a return, the value of the last expression in the body is returned, or NUL if there is none. Functions are values: pass them, return them, store them. A function may call itself.

fn square(x) {
x * x // implicit return
}
fn grade(score) {
if score >= 90 { return "A"; }
return "B";
}
let double = fn(x) { return x * 2; };
fn apply_twice(f, x) {
return f(f(x));
}
fn factorial(n) {
if n <= 1 { return 1; }
return n * factorial(n - 1);
}
assert square(5) == 25;
assert apply_twice(double, 3) == 12;
assert factorial(5) == 120;
print(grade(95));
ParameterSyntaxRule
RequiredxMust be passed
Default valuex = exprOptional; after the required parameters
Rest...xsLast; collects the remaining arguments into an Array (empty if none); no default

A default is evaluated at call time, in the function’s scope, so it may use earlier parameters — and, in a method, this. = NUL makes a parameter optional with no value.

fn range_step(start, end, step = 1) {
let out = #[];
let i = start;
loop {
if i >= end { break; }
out.push(i);
i += step;
}
return out;
}
assert range_step(0, 5).length() == 5;
assert range_step(0, 10, 2).length() == 5;
fn greet(name = NUL) {
if name != NUL { print("Hello, " + name); }
}
greet();

...xs in a parameter list collects arguments into an array. ...value in a call spreads an array or a tuple into arguments; it can be mixed with ordinary arguments, and spreading anything else is a type error. Spread is only for call arguments: #[...xs, 4] is a parse error.

fn sum(...nums) {
return nums.reduce(fn(acc, x) { return acc + x; }, 0);
}
fn add3(a, b, c) {
return a + b + c;
}
let values = #[1, 2, 3];
assert sum(...values) == 6;
assert sum() == 0;
assert add3(10, ...#[20, 30]) == 60;
assert add3(...(1, 2, 3)) == 6;
print(sum(1, 2, 3, 4));
CallRule
f(a, b)Positional arguments, in order
f(a, name: value)Named arguments, in any order, after all positional ones. The separator is :; f(name = value) is a parse error
Too few argumentsError, unless the missing parameters have defaults
Too many argumentsError, unless there is a rest parameter

Named arguments are not supported for built-in functions.

fn point(x = 0, y = 0, z = 0) {
return f"({x}, {y}, {z})";
}
assert point(z: 5) == "(0, 0, 5)";
assert point(1, z: 3) == "(1, 0, 3)";
print(point(y: 2, x: 1));

A function value captures the variables it references, by value, when it is created. Changing the outer variable afterwards does not change the closure. A closure may be returned, which makes factories:

fn make_adder(n) {
return fn(x) { return x + n; };
}
let add5 = make_adder(5);
assert add5(3) == 8;
let base = 100;
let get_base = fn() { return base; };
base = 999;
assert get_base() == 100;
print("captured");

A /// comment above a fn is its documentation; show(fn_name) prints the signature, with any annotations, and the documentation. The rules are in Comments.

/// Clamp a value to the given range.
/// Parameters:
/// value — the value to clamp
/// lo — lower bound
/// hi — upper bound
/// Returns: the clamped value
fn clamp_to(value: Number, lo: Number, hi: Number) -> Number {
if value < lo { return lo; }
if value > hi { return hi; }
return value;
}
show(clamp_to);