Skip to content

Functions and Closures

Kelvra already supports named functions, block function literals, expression-bodied lambdas, recursion, and closures with captured variables.

fn add(left i32, right i32) i32 {
return left + right
}

Named functions and methods can omit an explicit void return type:

fn logAnswer() {
print(42)
}

Function types appear directly in signatures and local bindings.

fn applyTwice(f fn(i32) i32, value i32) i32 {
return f(f(value))
}
var addOne fn(i32) i32 = fn(x i32) => x + 1

Expression-bodied lambdas are the compact form:

var addOne = fn(x i32) => x + 1

Block function literals are available when you want multiple statements:

var addOne fn(i32) i32 = fn(x) {
return x + 1
}

Parameter types still matter. The repo includes negative tests for missing parameter annotations and return-type mismatches.

Nested functions can capture outer locals.

fn makeAdder(base i32) fn(i32) i32 {
return fn(delta i32) => base + delta
}
var addTen = makeAdder(10)
print(addTen(32))

The test suite also covers closure mutation and deeper nested upvalue chains, so captured state is not limited to the simplest read-only case.

Named functions can call themselves normally. Several stress tests in the repo exercise recursive execution.

Kelvra’s type checker enforces return behavior:

  • returned values must match the declared return type
  • named functions without a value should omit the return type or use void
  • omitted return values on non-void paths are errors