Functions
Where a function looks up the names it uses is in Variables & Scope.
Defining a function
Section titled “Defining a function”| Form | Meaning |
|---|---|
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));Parameters
Section titled “Parameters”| Parameter | Syntax | Rule |
|---|---|---|
| Required | x | Must be passed |
| Default value | x = expr | Optional; after the required parameters |
| Rest | ...xs | Last; collects the remaining arguments into an Array (empty if none); no default |
Default values
Section titled “Default values”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();Rest parameters and spread
Section titled “Rest parameters and spread”...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));Calling
Section titled “Calling”| Call | Rule |
|---|---|
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 arguments | Error, unless the missing parameters have defaults |
| Too many arguments | Error, 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));Closures
Section titled “Closures”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");Documenting a function
Section titled “Documenting a function”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 valuefn clamp_to(value: Number, lo: Number, hi: Number) -> Number { if value < lo { return lo; } if value > hi { return hi; } return value;}show(clamp_to);