Functions
Named functions, hoisting, overloading by arity, anonymous functions, arrow syntax, closures, and dispatchers.
Named Functions
Functions are declared with the func keyword. They can accept any number of parameters and return a value with return.
func add(a, b) { return a + b } add(10, 20) // 30
If a function reaches the end of its body without a return, it implicitly returns null.
func greet(name) { print("Hello, " + name) // no return → returns null }
Hoisting
Named functions declared with func are hoisted — you can call them before their definition appears in the source code. The compiler lifts function declarations to the top of their enclosing scope.
// Call BEFORE definition — this works! var result = add(10, 20) // 30 func add(a, b) { return a + b } // Call AFTER definition also works add(5, 15) // 20
func are hoisted. Anonymous functions and arrow functions assigned to variables are not hoisted.Hoisting works within any scope — including inside blocks, loops, and other functions:
var sum = 0 for (var i = 0; i < 3; i = i + 1) { sum = sum + helper() // called before definition func helper() { return 10 } } // sum is 30
Overloading by Arity
Zym supports function overloading based on the number of parameters (arity). Multiple functions with the same name but different arities can coexist. The correct version is dispatched at call time.
func process() { return "No arguments" } func process(x) { return x * 2 } func process(x, y) { return x + y } func process(x, y, z) { return x + y + z } process() // "No arguments" process(5) // 10 process(3, 7) // 10 process(1, 2, 3) // 6
Hoisting with overloading
Overloaded functions are all hoisted independently. You can call any arity variant before any of them are defined.
// Call before definition compute() // 0 compute(10) // 100 compute(5, 3) // 15 func compute() { return 0 } func compute(x) { return x * x } func compute(x, y) { return x * y }
Anonymous Functions
Functions without a name can be assigned to variables or passed as arguments. They use the same func keyword but without a name.
var double = func (x) { return x * 2 } double(5) // 10
Anonymous functions are not hoisted. They must be assigned before they can be called.
As function arguments
func apply(fn, value) { return fn(value) } apply(func (x) { return x + 1 }, 10) // 11
Arrow Functions
Arrow functions are a concise syntax for anonymous functions using =>.
// Expression body (implicit return) var double = (x) => x * 2 double(5) // 10 // Block body (explicit return) var process = (x, y) => { var sum = x + y return sum * 2 } // No parameters var getPi = () => 3.14159 // Single parameter (parentheses optional) var square = x => x * x
=> expr, the expression is implicitly returned. With => { ... }, you must use an explicit return.Closures
Functions capture variables from their enclosing scope. These captured variables (upvalues) remain accessible even after the outer function returns.
func makeCounter() { var count = 0 return func () { count = count + 1 return count } } var counter = makeCounter() counter() // 1 counter() // 2 counter() // 3
Independent closure state
Each call to the outer function creates a new set of captured variables. Multiple closures from the same factory are fully independent.
var c1 = makeCounter() var c2 = makeCounter() c1() // 1 c1() // 2 c2() // 1 (independent state) c1() // 3
Shared closure state
Multiple closures returned from the same call share the same captured variables.
func makeBox(initial) { var value = initial var getter = func () { return value } var setter = func (v) { value = v } return [getter, setter] } var box = makeBox(10) var get = box[0] var set = box[1] get() // 10 set(42) get() // 42 — both closures share 'value'
Closures capturing loop variables
Be aware that closures capture the variable itself, not a snapshot of its value at creation time.
var funcs = [] for (var i = 0; i < 3; i = i + 1) { var captured = i // new variable each iteration push(funcs, func () { return captured }) } funcs[0]() // 0 funcs[1]() // 1 funcs[2]() // 2
Dispatchers
When you return an overloaded function from another function, Zym bundles all the arity variants into a single dispatcher object. The dispatcher automatically resolves to the correct overload at the call site based on the number of arguments.
func makeAdder() { func add(x) { return x + 10 } func add(x, y) { return x + y } return add // returns a dispatcher with both overloads } var adder = makeAdder() adder(5) // 15 (calls 1-arg version) adder(3, 7) // 10 (calls 2-arg version)
Dispatchers with closures
Overloaded functions inside a factory can capture shared state, and the returned dispatcher preserves all closures.
func makeAccumulator() { var total = 0 func acc() { return total } func acc(x) { total = total + x return total } return acc } var acc = makeAccumulator() acc(10) // 10 acc(20) // 30 acc() // 30 (read without adding)
Higher-order dispatchers
Dispatchers can be passed as arguments to other functions, stored in collections, or used anywhere a function value is expected.
func makeProcessor() { func proc(x) { return x * 2 } func proc(x, y) { return x + y } return proc } var p = makeProcessor() // Store in a list var ops = [p] ops[0](5) // 10 ops[0](3, 7) // 10 // Pass to another function func apply(fn, val) { return fn(val) } apply(p, 5) // 10
Multiple dispatchers
A factory can create and return multiple independent dispatchers.
func makeOps() { func math(x) { return x * x } func math(x, y) { return x + y } func text(s) { return "[" + s + "]" } func text(a, b) { return a + " " + b } return [math, text] } var ops = makeOps() ops[0](5) // 25 (math/1) ops[0](3, 4) // 7 (math/2) ops[1]("hi") // "[hi]" (text/1) ops[1]("hello", "world") // "hello world" (text/2)
Functions as Values
Functions are first-class values. They can be stored in variables, lists, maps, and struct fields, passed as arguments, and returned from other functions.
// Store in a map var ops = { add: func (a, b) { return a + b }, mul: func (a, b) { return a * b } } ops.add(3, 4) // 7 ops.mul(3, 4) // 12 // Store in a list var transforms = [ (x) => x * 2, (x) => x + 1, (x) => x * x ] transforms[0](5) // 10 transforms[2](4) // 16 // Higher-order: function that returns a function func multiplier(factor) { return (x) => x * factor } var triple = multiplier(3) triple(7) // 21