Declaration
fn function_name(param1, param2) {
// body
return value
}
- Parameters are positional; no default values.
returnexits the function and optionally produces a value.- A function without
returnreturnsnull. - Functions are values — they can be stored in variables and passed as arguments.
fn add(a, b) {
return a + b
}
let result = add(10, 20)
print(result) // 30
Recursion
fn factorial(n) {
if n <= 1 {
return 1
}
return n * factorial(n - 1)
}
print(factorial(6)) // 720
Functions as Values
fn double(x) {
return x * 2
}
let transform = double
print(transform(7)) // 14
Closures and Nested Functions
Functions in Joa use lexical scoping — they capture the environment at the point of their definition. This makes closures possible:
fn make_counter(start) {
let count = start
fn increment() {
count += 1
return count
}
return increment
}
let counter = make_counter(0)
print(counter()) // 1
print(counter()) // 2
print(counter()) // 3
Each call to make_counter() creates an independent closure with its own count variable. The inner function increment captures count from its enclosing scope and can both read and update it.
Higher-Order Functions
fn apply(f, value) {
return f(value)
}
fn square(x) {
return x ** 2
}
print(apply(square, 5)) // 25.0
Argument Count
Calling a function with the wrong number of arguments raises a Runtime Error with a clear message:
fn greet(name) { return "Hello " + name }
greet()
// ❌ Runtime Error: Function 'greet' expects 1 argument(s), but got 0.
// 💡 Hint: Pass exactly 1 argument(s) to 'greet'.