Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Functions

fun name(params) { ... } declares a function. return gives its value; a function that ends without one returns nil.

fun total(items) {
    let mut sum = 0;
    for item in items { sum += item.price * item.qty; }
    return sum;
}

print(total([{ price: 10, qty: 2 }, { price: 5, qty: 1 }])); // 25

A function captures the variables around it, and keeps them after the enclosing call returns:

fun counter() {
    let mut n = 0;
    return () => { n += 1; return n; };
}

let next = counter();
next();
print(next()); // 2

Calling with the wrong number of arguments is an ArgumentError:

fun area(w, h) { return w * h; }
area(3); // ArgumentError: Expected 2 arguments but got 1.

Anonymous Functions

fun without a name is an expression, so functions can be passed around inline.

let prices = [120, 45, 300];
print(prices.map(fun (p) { return p * 2; })); // [240, 90, 600]

Arrow functions are shorthand: an expression body is returned, a block body works like fun’s.

let add = (a, b) => a + b;
let double = x => x * 2;
let log = msg => { print("[log] " + msg); };

print(add(2, 3));  // 5
print(double(21)); // 42
log("started");    // [log] started

A single plain parameter needs no parentheses. Parameters may be mut or patterns:

let users = [{ name: "ann", age: 31 }, { name: "bob", age: 17 }];

print(users.map(({ name }) => name));                    // ["ann", "bob"]
print(users.filter(u => u.age >= 18).map(u => u.name));  // ["ann"]

let bump = (mut n) => { n += 1; return n; };
print(bump(1)); // 2

A body in braces is an object when it looks like one: () => { a: 1 } and x => { x } return an object.

let origin = () => { x: 0, y: 0 };
let wrap = name => { name };

print(origin());     // {x: 0, y: 0}
print(wrap("ann"));  // {name: "ann"}

Default Parameters

A parameter may have a default, name = expression, taken when the call passes no argument for it.

fun connect(host, port = 5432) {
    return "{host}:{port}";
}

print(connect("db.local"));       // db.local:5432
print(connect("db.local", 6379)); // db.local:6379

The default is evaluated at each such call, after the parameters before it, which it may use, so a mutable default is a new value every call. An explicit nil is an argument and gets no default.

fun greet(name, greeting = "hi", punct = greeting == "hi" ? "!" : ".") {
    return "{greeting} {name}{punct}";
}

print(greet("ann"));          // hi ann!
print(greet("ann", "hello")); // hello ann.
print(greet("ann", nil));     // nil ann.

fun tag(item, mut tags = []) { tags.push(item); return tags; }
print(tag("a")); // ["a"]
print(tag("b")); // ["b"]

Every parameter after one with a default needs one too, except a rest parameter; self and a rest parameter cannot have one.

fun f(a = 1, b) {} // Error at 'b': A parameter without a default cannot follow one with a default.

Arrow functions take defaults too:

let inc = (n, by = 1) => n + by;
print(inc(5));     // 6
print(inc(5, 10)); // 15