Static copy
Copied from docs/en/manual/functions-and-closures.md in https://github.com/poppy-team/aipo-lang (MIT). Pinned to revision ce47c4e070404fc41722ccd8e2b7b808b99062a8, blob d9cccc196ec0181ea5be58a5b531b75ea2d0a4bc. The source repository remains canonical; this copy is refreshed through a sync pull request, not live.
Functions in Aipo are first-class citizens. They can be passed as arguments, assigned to variables, returned by other functions, and capture values from their enclosing lexical scope.
Functions are declared using the fn keyword and enclosed in curly braces { ... }:
fn add(a, b) {
return a + b
}
let total = add(10, 20)
io.println(total) # 30If return is omitted or does not specify an expression, the function returns none.
Aipo allows default fallback values for optional parameters:
fn connect(host, port = 8080, timeout = 5000) {
io.println(f"Connecting to {host}:{port} with timeout {timeout}ms")
}
connect("localhost") # uses port 8080 and timeout 5000
connect("db.internal", 5432) # uses port 5432 and timeout 5000Functions can also be called with named arguments for enhanced readability:
connect("api.service", timeout = 1000)Anonymous functions (fn(params) { ... }) can be assigned to variables and retain lexical access to captured variables:
fn create_counter(initial = 0) {
var count = initial
return fn() {
count += 1
return count
}
}
let c = create_counter(10)
io.println(c()) # 11
io.println(c()) # 12Aipo's VM implements safe shared upvalues, ensuring that mutations to captured variables are reflected across all sharing closures.
=>) For concise single-expression callbacks (particularly in collection operations like map and filter), Aipo provides arrow syntax:
let numbers = [1, 2, 3, 4, 5]
# Double values with single-parameter lambda
let doubled = numbers.map(x => x * 2)
io.println(doubled) # [2, 4, 6, 8, 10]
# Filter evens
let evens = numbers.filter(x => x % 2 == 0)
io.println(evens) # [2, 4]Multi-parameter lambdas use parenthesized parameter lists: (a, b) => a + b.
Aipo supports local functions defined inside other function bodies, with full self-recursion resolution via FillSelfCapture:
fn factorial(n) {
fn loop_rec(current, acc) {
if current <= 1 {
return acc
}
return loop_rec(current - 1, acc * current)
}
return loop_rec(n, 1)
}
io.println(factorial(5)) # 120