Multiple Returns and Recursion
In Zolo, multiple return is modeled as a homogeneous array: the return
type is [T] and the caller destructures with let (a, b) = f(). This
avoids creating a struct just to group two or three related values:
divmod, min_max and version — positional destructuring.
// Feature: Multiple return via array
// Syntax: `fn f() -> [T] { return [a, b, c] }` + `let (x, y, z) = f()`
// When to use: return two or three related values without
// creating a struct — the classic div/mod, min/max, ok+value.
//
// In Zolo, "tuples" at the value site are arrays: the return type
// is `[T]` (homogeneous array) and destructuring extracts by position.
fn divmod(a: int, b: int) -> [int] {
return [a / b, a % b]
}
let (q, r) = divmod(17, 5)
print("{q} remainder {r}") // 3 remainder 2
// Min/max in a single pass.
fn min_max(arr: [int]) -> [int] {
var lo = arr[0]
var hi = arr[0]
for x in arr {
if x < lo { lo = x }
if x > hi { hi = x }
}
return [lo, hi]
}
let (lo, hi) = min_max([3, 1, 4, 1, 5, 9, 2, 6])
print("lo={lo} hi={hi}") // lo=1 hi=9
// Triples work the same — destructure by position.
fn version() -> [int] {
return [1, 2, 3]
}
let (maj, min, patch) = version()
print("v{maj}.{min}.{patch}") // v1.2.3
// For heterogeneous types, use a typed array as `[any]`-like
// (without annotation the inferrer accepts mixed literals).
fn lookup(ok: bool) -> [str] {
if ok {
return ["ok", "all good"]
}
return ["err", "went wrong"]
}
let (status, msg) = lookup(true)
print("{status}: {msg}") // ok: all good
Recursion works naturally: a function can call itself, and the compiler resolves references in any order. For cases where the call depth would be high, the accumulator form (tail-style) is preferred. Mutual recursion — two functions that call each other — is also supported:
Factorial, naive Fibonacci and with accumulator, mutually recursive is_even/is_odd.
// Feature: Recursion (direct and mutual)
// Syntax: `fn f() { ...f()... }` — fn can call itself.
// When to use: naturally recursive structures (trees,
// lists), divide-and-conquer, mathematical definitions.
// Classic factorial.
fn factorial(n: int) -> int {
if n <= 1 { return 1 }
return n * factorial(n - 1)
}
print(factorial(5)) // 120
print(factorial(10)) // 3628800
// Naive Fibonacci (just for show, not efficient).
fn fib(n: int) -> int {
if n < 2 { return n }
return fib(n - 1) + fib(n - 2)
}
for i in 0..10 {
print("fib({i}) = {fib(i)}")
}
// Fibonacci with accumulator ("tail-style" recursion).
fn fib_iter(n: int, a: int, b: int) -> int {
if n == 0 { return a }
return fib_iter(n - 1, b, a + b)
}
fn fib_fast(n: int) -> int {
return fib_iter(n, 0, 1)
}
print(fib_fast(20)) // 6765
// MUTUAL recursion — two functions calling each other.
fn is_even(n: int) -> bool {
if n == 0 { return true }
return is_odd(n - 1)
}
fn is_odd(n: int) -> bool {
if n == 0 { return false }
return is_even(n - 1)
}
print(is_even(10)) // true
print(is_odd(7)) // true
// Sum of array via recursion.
fn sum_arr(arr: [int], i: int) -> int {
if i >= arr.len() { return 0 }
return arr[i] + sum_arr(arr, i + 1)
}
print(sum_arr([1, 2, 3, 4, 5], 0)) // 15
Challenge
Write powers(base: int, max: int) -> [int] that returns [base^0, base^1, ..., base^max]
using recursion, and destructure the first three results when calling it.
See also