Functions
Defining functions
fn add(a, b) {
return a + b
}
print(add(2, 3)) // 5
A function with no return returns nil. return with no value also
returns nil.
Anonymous functions
Functions are first-class values:
var doubler = fn(n) {
return n * 2
}
print(doubler(21)) // 42
var square = fn(x) { return x * x }
print(square(5)) // 25
Arguments
Missing arguments default to nil; too many arguments is an error:
fn add(a, b) { return a + b }
print(add(1, 2, 3)) // error: too many arguments
Missing arguments default to nil:
fn greet(name, title) {
return title + " " + name
}
print(greet("Smith")) // nil Smith
print(greet("Smith", "Dr")) // Dr Smith
Functions as data
var ops = {
add: fn(a, b) { return a + b },
mul: fn(a, b) { return a * b },
}
print(ops.add(10, 3)) // 13 (dot notation for members)
print(ops.mul(4, 5)) // 20
Returning multiple values
A function returns a single value, but that value can be an array, and a destructuring declaration unpacks it into several variables in one step:
fn divmod(a, b) {
return [a / b, a % b]
}
var [c, e] = divmod(17, 5) // c is 3, e is 2
print(c, e) // 3 2
fn stats(list) {
var total = 0
for (v : list) { total += v }
return [total / len(list), total]
}
var [avg, sum] = stats([10, 20, 30, 40]) // avg 25, sum 100
print(avg, sum) // 25 100
Rules for unpacking an array:
var [a, b] = [1, 2] // a is 1, b is 2
var [first] = [9, 8] // extra elements are ignored (first is 9)
// var [x, y] = [1] // error: not enough elements
You cannot destructure an object
Destructuring works on arrays only. Returning an object and trying to unpack it as a pair is an error:
fn get_pair() {
return {this: "value"}
}
var [v, c] = get_pair()
// error: destructuring requires an array value, got object
To return named values, return an object and access it by key:
fn get_pair() {
return {v: 3, c: 5}
}
var pair = get_pair()
print(pair.v, pair.c) // 3 5
Each element of the pattern must be a plain name — there are no nested or
ref patterns (var [{a, b}] = ... is not supported).
This is the idiomatic way to return "multiple values" — the function returns
an array, the caller destructures it. The destructuring declares var /
const / local bindings (but not ref, which cannot destructure), and it
works on any array-valued expression, not just function calls.
Higher-order functions work naturally:
fn apply_twice(f, x) {
return f(f(x))
}
print(apply_twice(square, 2)) // 16
Recursion
Recursion depth is capped at 256 per execution context; exceeding it
raises a clean STACK_OVERFLOW error.
fn fact(n) {
if (n <= 1) { return 1 }
return n * fact(n - 1)
}
print(fact(6)) // 720
Globals, not lexical closures
Functions read and write global variables directly, and there is no lexical capture of enclosing locals:
var counter = 0
fn increment() {
counter++
}
increment()
print(counter) // 1
fn broken() {
var inner = 42
return fn() { return inner } // error: inner is not visible here
}
This matters for callbacks and async tasks — reach data through globals or pass it as arguments.
Scope summary
- Top-level
var/constdeclarations are global. var/constinside a function are local to that call.- Blocks (
if/while/for/else, standalone{}) create new scopes; variables declared inside are local to that block. - For-loop init/condition/increment run in the enclosing scope.
- There are no lexical closures — functions see only globals, not enclosing locals (see Globals, not lexical closures).