Functions
Functions are data components, exactly like fields — attached to a node, reachable by dot-path, and first-class values you can store, pass around, and call from anywhere with access to them.
Declaring & Returning
The last expression in a function, without a trailing ;, is its return value — return also works, for an early exit:
fn add(a: float, b: float) -> float {
a + b
}
fn greet(name: str = "world") -> str {
return `Hello, ${name}!`;
}
#[main]
fn main() {
pln(self.add(2, 3));
pln(self.greet());
pln(self.greet("Stof"));
}One gotcha worth knowing: a semicolon after that last expression changes its meaning. fn f() -> str { 42; } doesn't return 42 — the ; turns it into a statement, so the function returns nothing, and a declared -> str return type with nothing to return is an error. Drop the trailing ; on whatever line should be the return value or use a return statement.
Optional & Named Parameters
A ? suffix on a parameter's type makes it optional; calling with name = value passes arguments by name instead of position, in any order:
fn create(id: str!, expires?: ms) {
pln(id, expires);
}
fn total(a: int, b: int) -> int {
a + b
}
#[main]
fn main() {
self.create('abc123');
self.create('abc123', 5000ms);
pln(self.total(b = 30, a = 12));
}Arrow Functions
Shorthand for a function as an expression — as a local variable, or directly as a field's value:
double: (x: int): int => x * 2
#[main]
fn main() {
const add = (a: int, b: int): int => a + b;
pln(add(5, 5));
pln(self.double(21));
}Functions as Fields
A field can hold a function directly — call it the same way you'd call any other function:
name: "Bob Smith"
message: (name: str = self.name): str => `Hi, ${name}`
#[main]
fn main() {
pln(self.message());
pln(self.message("Ada"));
}message's default parameter reads self.name at call time — so calling it with no arguments still resolves against whatever self is at that point.
Recursion with this
this refers to the function currently running — useful for recursion without hardcoding the function's own name:
fn fibonacci(n: int) -> int {
if (n <= 1) { n }
else { this.call(n - 1) + this(n - 2) }
}
#[main]
fn main() {
pln(self.fibonacci(10));
}What's Not Here Yet
Functions can also carry custom attributes (#[my_attr]), run asynchronously (async fn, #[async]), and be called on a prototype directly for static-like behavior via <TypeName>.func() — each of those gets its own dedicated page.