Functions
Named functions
Section titled “Named functions”Define functions with the fn keyword.
fn greet() { PRINT "Hello, RESONON!";}greet();
fn add(a, b) { return a + b;}PRINT add(3, 4); // 7Early return
Section titled “Early return”Use return to exit a function early.
fn absolute(x) { if x < 0 { return -x; } return x;}
fn grade(score) { if score >= 90 { return "A"; } if score >= 80 { return "B"; } if score >= 70 { return "C"; } return "F";}Implicit return
Section titled “Implicit return”The last expression in a function body is its return value when no explicit return is used.
fn square(x) { x * x}PRINT square(5); // 25Functions returning patterns
Section titled “Functions returning patterns”Functions can return patterns just like any other value.
fn c_major() { return [C4 E4 G4];}PRINT c_major();Anonymous functions
Section titled “Anonymous functions”Assign a function literal to a variable.
let double = fn(x) { return x * 2; };PRINT double(7); // 14
let is_even = fn(n) { return n % 2 == 0; };PRINT is_even(4); // trueClosures
Section titled “Closures”Anonymous functions capture their enclosing environment at creation time.
let multiplier = 10;let times_ten = fn(x) { return x * multiplier; };PRINT times_ten(5); // 50Capture is by value — changing the outer variable after creation does not affect the closure:
let base = 100;let get_base = fn() { return base; };base = 999;PRINT get_base(); // 100 (still the original value)Returning closures (factory pattern)
Section titled “Returning closures (factory pattern)”fn make_adder(n) { return fn(x) { return x + n; };}let add5 = make_adder(5);let add10 = make_adder(10);PRINT add5(3); // 8PRINT add10(3); // 13Nested closures
Section titled “Nested closures”fn make_linear(m) { return fn(b) { return fn(x) { return m * x + b; }; };}let line = make_linear(2)(3); // y = 2x + 3PRINT line(0); // 3PRINT line(5); // 13Selective capture
Section titled “Selective capture”Closures only capture variables they actually reference, not the entire enclosing scope.
let x = 10;let y = 20;
let use_x = fn() { return x; }; // captures only xlet use_y = fn() { return y; }; // captures only y
PRINT use_x(); // 10PRINT use_y(); // 20Closures as arguments
Section titled “Closures as arguments”Anonymous functions can be passed directly as arguments without assigning them to a variable first.
fn apply(func, value) { return func(value);}
PRINT apply(fn(x) { return x * x; }, 5); // 25This pattern is used extensively with array methods like .map() and .filter() — see Arrays and the Higher-order functions section below.
Functions as values
Section titled “Functions as values”Functions are first-class values — they can be passed as arguments and returned from other functions.
fn apply_twice(func, x) { return func(func(x));}
let double = fn(x) { return x * 2; };PRINT apply_twice(double, 3); // 12Higher-order functions
Section titled “Higher-order functions”Arrays provide .map(), .filter(), and .reduce() for functional-style processing. See Arrays for details.
let data = #[1, 2, 3, 4, 5];
data.map(fn(x) { return x * 2; });// [2, 4, 6, 8, 10]
data.filter(fn(x) { return x % 2 == 0; });// [2, 4]
data.reduce(fn(acc, x) { return acc + x; }, 0);// 15Recursion
Section titled “Recursion”Functions can call themselves.
fn factorial(n) { if n <= 1 { return 1; } return n * factorial(n - 1);}PRINT factorial(5); // 120
fn fibonacci(n) { if n <= 1 { return n; } return fibonacci(n - 1) + fibonacci(n - 2);}PRINT fibonacci(10); // 55Rest parameters
Section titled “Rest parameters”Use ...name as the last parameter to collect any remaining arguments into an array.
fn log(level, ...messages) { PRINT level; for msg in messages { PRINT " " + msg; }}
log("INFO", "server started", "port 8080");// INFO// server started// port 8080
log("WARN");// WARN (messages is an empty array)Spread arguments
Section titled “Spread arguments”Use ...array in a function call to unpack an array into individual arguments.
fn add3(a, b, c) { return a + b + c;}
let nums = #[10, 20, 30];PRINT add3(...nums); // 60Spread can be mixed with regular arguments:
let pair = #[2, 3];PRINT add3(1, ...pair); // 6Round-trip with rest parameters
Section titled “Round-trip with rest parameters”Rest parameters and spread arguments pair naturally:
fn sum(...nums) { return nums.reduce(fn(acc, x) { return acc + x; }, 0);}
let values = #[1, 2, 3, 4, 5];PRINT sum(...values); // 15Reference parameters
Section titled “Reference parameters”Use &name to accept a parameter by reference, allowing the function to modify the original value.
fn append_note(&arr, note) { arr.push(note);}
let melody = #[C4, D4, E4];append_note(&melody, F4);PRINT melody; // [C4, D4, E4, F4]Type annotations
Section titled “Type annotations”Parameters and return values can be annotated with types.
fn add(a: Number, b: Number) -> Number { return a + b;}
fn greet(name: String) -> String { return "Hello, " + name;}Function types
Section titled “Function types”Higher-order functions can annotate their function parameters:
fn apply(f: (Number) -> Number, x: Number) -> Number { return f(x);}
PRINT apply(fn(n) { return n * 2; }, 5); // 10Doc comments
Section titled “Doc comments”Use /// to attach documentation to functions and variables.
/// Clamp a value to the given range./// Returns lo if value < lo, hi if value > hi.fn clamp(value, lo, hi) { if value < lo { return lo; } if value > hi { return hi; } return value;}Multiple consecutive /// lines are joined together. Doc comments are visible via the show() introspection function:
show(clamp);// Function: clamp(value, lo, hi)// "Clamp a value to the given range.\nReturns lo if value < lo, hi if value > hi."Private functions
Section titled “Private functions”Mark functions and variables as private to prevent them from being exported by a module.
private fn helper(x) { return x * 2;}
fn public_api(x) { return helper(x) + 1;}When this file is loaded via use, only public_api is accessible — helper is hidden.
use "mylib";mylib.public_api(5); // 11mylib.helper(5); // Error: 'helper' is privateprivate also works with let:
private let INTERNAL_CONSTANT = 42;Default parameter values
Section titled “Default parameter values”Parameters can have default values using = expr. When a caller omits those arguments, the defaults are used instead.
fn greet(name, greeting = "Hello") { return greeting + ", " + name + "!";}
PRINT greet("Alice", "Hi"); // Hi, Alice!PRINT greet("Bob"); // Hello, Bob!Defaults are evaluated at call time in the function scope, so they can reference earlier parameters:
fn range_step(start, end, step = 1) { let result = Array(); let i = start; loop { if i >= end { break; } result.push(i); i = i + step; } return result;}
PRINT range_step(0, 10, 2); // [0, 2, 4, 6, 8]PRINT range_step(0, 5); // [0, 1, 2, 3, 4]Defaults work with closures too:
let amplify = fn(x, factor = 2) { return x * factor; };PRINT amplify(5); // 10PRINT amplify(5, 3); // 15Named arguments
Section titled “Named arguments”Pass arguments by name at the call site using name: value syntax. This lets you skip optional parameters and provide arguments in any order.
fn point(x = 0, y = 0, z = 0) { PRINT f"({x}, {y}, {z})";}
point(1, 2, 3); // (1, 2, 3)point(z: 5); // (0, 0, 5)point(y: 2, x: 1); // (1, 2, 0)Named arguments can be mixed with positional arguments — positional arguments must come first:
fn create_track(name, channel, velocity = 100, port = NUL) { PRINT f"{name} ch={channel} vel={velocity} port={port}";}
create_track("drums", 10); // positional onlycreate_track("synth", 2, port: "USB"); // skip velocity, set portcreate_track("bass", 1, velocity: 80); // skip port, set velocity