Skip to content

Functions

fn add(a: Int, b: Int): Int {
a + b
}
  • The last expression in the body is the return value (no return needed).
  • All module-level functions are hoisted. You can call them before they’re defined.
  • Trailing commas are allowed in parameter lists and argument lists.

Every parameter can be passed by name at the call site:

fn greet(name: String, prefix: String): String {
"${prefix}, ${name}!"
}
debug greet(name: "Alice", prefix: "Hello") // Hello, Alice!
debug greet(prefix: "Hi", name: "Bob") // Hi, Bob!. Any order
debug greet("Carol", "Hey") // positional also works
fn greet(name: String, prefix: String = "Hello"): String {
"${prefix}, ${name}!"
}
debug greet(name: "Alice") // Hello, Alice!
debug greet(name: "Bob", prefix: "Hi") // Hi, Bob!

Private functions don’t need an explicit return type. The compiler infers it from the body:

fn double(n: Int) { n * 2 } // inferred: Int
fn noop() { } // inferred: Void

pub fn must always have an explicit return type. Module boundaries must be self-documenting.

Functions are first-class values. Anonymous functions (closures) capture their surrounding scope:

let add5 = fn(x: Int): Int { x + 5 }
debug add5(10) // 15
fn make_adder(base: Int): fn(Int): Int {
fn(x: Int): Int { base + x } // captures 'base' from enclosing scope
}
let add10 = make_adder(10)
debug add10(3) // 13
debug add10(7) // 17

|> passes the left-hand value as the first argument to the right-hand function:

fn double(n: Int): Int { n * 2 }
fn inc(n: Int): Int { n + 1 }
debug 5 |> double // 10
debug 5 |> double |> inc // 11

Use _ to control where the piped value lands:

fn add(a: Int, b: Int): Int { a + b }
debug 3 |> add(_, 10) // add(3, 10) = 13
debug 3 |> add(10, _) // add(10, 3) = 13
fn identity<T>(x: T): T { x }
debug identity(42) // 42
debug identity("hello") // hello