Named Functions
A named function is declared with fn, takes typed parameters, and can
declare the return type explicitly or let inference do the work.
Functions can be declared in any order — the compiler makes two
passes, so calling a function defined further below is valid:
Basic declaration, inferred return, and a function with no return (implicitly nil).
// Feature: Named functions
// Syntax: `fn name(p: T, ...) -> R { ... }`
// When to use: bread-and-butter — a reusable block, top-level
// or nested, with typed parameters and an optional return.
// Function with explicit types on parameters and return.
fn add(a: int, b: int) -> int {
return a + b
}
print(add(2, 3)) // 5
// Return type can be omitted — inference handles it.
fn double(x: int) {
return x * 2
}
print(double(7)) // 14
// No return: "void" function (returns implicit nil).
fn shout(msg: str) {
print("!! {msg} !!")
}
shout("zolo") // !! zolo !!
// Functions can be declared in any order — the
// compiler does two passes. `top` calls `helper`
// declared after it just fine.
fn top() -> int {
return helper(10)
}
fn helper(n: int) -> int {
return n + 1
}
print(top()) // 11
Sometimes auxiliary logic only makes sense inside another function. Using a local lambda keeps the scope clean and makes it clear that the helper is an implementation detail:
Local helpers via lambda — access to the outer scope without polluting the global namespace.
// Feature: Nested functions (local helpers)
// Syntax: `let helper = |...| { ... }` inside another fn,
// or simply declaring and using it immediately.
// When to use: a private helper that only makes sense inside
// the scope of another function — without polluting the global
// namespace.
// Local helper via lambda — scope restricted to `outer`.
fn outer(x: int) -> int {
let inner = |y: int| y * 2
return inner(x) + 1
}
print(outer(5)) // 11
// Helper that uses variables from the outer scope.
fn process(items: [int]) -> int {
var total = 0
let add = |n| {
total = total + n
}
for it in items {
add(it * 2)
}
return total
}
print(process([1, 2, 3, 4])) // 20
// Decompose steps with local helpers (readability).
fn render_user(name: str, age: int) -> str {
let header = |n| "[USER: {n}]"
let body = |a| " age = {a}"
let h = header(name)
let b = body(age)
return "{h}\n{b}"
}
print(render_user("Alice", 30))
// expected:
// [USER: Alice]
// age = 30
// Recursive helper via closure.
fn count_down(from: int) {
let step = |n: int| {
if n < 0 { return }
print(n)
}
var i = from
while i >= 0 {
step(i)
i -= 1
}
}
count_down(3)
// expected: 3 2 1 0
Challenge
Create a function clamp(x: int, lo: int, hi: int) -> int that ensures
lo <= result <= hi. Test it with values below, within, and above the range.
See also