Functions & Closures
Functions are first-class values in Achronyme. They can be assigned to variables, passed as arguments, and returned from other functions.
Function Declarations
Section titled “Function Declarations”Define named functions with fn name(params) { body }.
fn add(a, b) { return a + b}assert(add(3, 4) == 7)
fn greet() { return "hello"}assert(greet() == "hello")Anonymous Functions
Section titled “Anonymous Functions”Create unnamed functions with fn(params) { body } and assign them to variables.
let double = fn(x) { x * 2 }assert(double(5) == 10)
let mul = fn(a, b) { return a * b }assert(mul(3, 7) == 21)Return Values
Section titled “Return Values”The last expression in a function body is its return value. Use return for early exits.
fn square(x) { x * x}assert(square(4) == 16)
fn abs(x) { if x >= 0 { return x } return -x}assert(abs(-5) == 5)Functions without a return and no trailing expression return nil.
Closures
Section titled “Closures”Functions capture variables from their enclosing scope by reference. Changes to captured variables are visible to all closures that share them.
fn make_adder(n) { return fn(x) { x + n }}let add5 = make_adder(5)assert(add5(3) == 8)Closures can capture and mutate state:
fn make_counter() { mut count = 0 return fn() { count = count + 1 return count }}let counter = make_counter()assert(counter() == 1)assert(counter() == 2)assert(counter() == 3)Multiple closures can share the same mutable variable:
fn make_pair() { mut val = 0 let getter = fn() { val } let setter = fn(x) { val = x } return [getter, setter]}let pair = make_pair()let get = pair[0]let set = pair[1]assert(get() == 0)set(42)assert(get() == 42)Higher-Order Functions
Section titled “Higher-Order Functions”Functions can accept and return other functions.
fn apply(f, x) { return f(x)}assert(apply(fn(x) { x * x }, 5) == 25)fn compose(f, g) { return fn(x) { f(g(x)) }}let inc = fn(x) { x + 1 }let dbl = fn(x) { x * 2 }let inc_then_dbl = compose(dbl, inc)assert(inc_then_dbl(3) == 8)Common patterns like map, filter, and reduce:
fn filter(arr, pred) { mut result = [] for x in arr { if pred(x) { push(result, x) } } return result}let evens = filter([1, 2, 3, 4, 5, 6], fn(x) { x % 2 == 0 })assert(len(evens) == 3)
fn reduce(arr, init, f) { mut acc = init for x in arr { acc = f(acc, x) } return acc}assert(reduce([1, 2, 3, 4, 5], 0, fn(a, b) { a + b }) == 15)Recursion
Section titled “Recursion”Named functions can call themselves. Use named function expressions (fn name(params) { ... }) when assigning a recursive function to a variable.
fn factorial(n) { if n <= 1 { return 1 } return n * factorial(n - 1)}assert(factorial(5) == 120)
let fib = fn fib(n) { if n < 2 { return n } return fib(n - 1) + fib(n - 2)}assert(fib(10) == 55)Type Annotations
Section titled “Type Annotations”Functions can include optional type annotations on parameters and return types:
fn add(a: Field, b: Field) -> Field { return a + b}
fn is_positive(x: Field) -> Bool { return x > 0}
let double = fn(x: Field) -> Field { x * 2 }Mixed typed and untyped parameters are allowed — this is gradual typing:
fn scale(x: Field, factor) { x * factor}In circuit mode, annotations are checked at compile time. See Type Annotations for details on type checking rules and constraint savings.
Quick Reference
Section titled “Quick Reference”| Feature | Syntax | Notes |
|---|---|---|
| Named function | fn name(a, b) { body } | Hoisted in scope |
| Anonymous function | fn(a, b) { body } | First-class value |
| Named function expression | let f = fn f(n) { ... } | Enables recursion via variable |
| Typed parameters | fn f(x: Field, y: Bool) | Optional, checked in circuits |
| Return type | fn f(x) -> Field { body } | Optional, checked in circuits |
| Implicit return | last expression in body | No return needed |
| Explicit return | return expr | Early exit |
| Closure capture | automatic | By reference |