Functions & closures
A function is declared with func <name> = define(<params>) { ... } - func and define are two separate keywords either side of the =, not one combined keyword.
func add = define(a, b) {
return a + b;
}
output(add(2, 3)); // 5
Functions are first-class values: you can store one in a variable, pass it as an argument, or return it, exactly like any other value - a func declaration is really sugar for let <name> = define(...) {...};.
Default parameter values
Any parameter can have a default, using the same = syntax as a variable declaration. Once one parameter has a default, every parameter after it must too - this is enforced at parse time.
func greet = define(name, greeting = "Hello") {
return greeting + ", " + name + "!";
}
output(greet("Ada")); // "Hello, Ada!"
output(greet("Ada", "Hi")); // "Hi, Ada!"
func bad = define(a = 1, b) { return a + b; }
// parse error: Cannot have non default parameter 'b' after default parameters
Calls are strictly checked
Falx does not support variadic parameters or "extra arguments are ignored" - every call must supply exactly as many arguments as the function has parameters that lack a default (up to the total parameter count if you provide the optional ones too). Calling with the wrong count is a runtime error, not a silent truncation:
func add = define(a, b) { return a + b; }
output(add(1)); // runtime error: Expected 2 arguments, but got 1
output(add(1, 2, 3)); // runtime error: Expected 2 arguments, but got 3
Closures
A func declared inside another function closes over that function's local scope, including variables declared with let. Each call to the outer function creates a fresh, independent scope:
func makeCounter = define() {
let count = 0;
func increment = define() {
count = count + 1; // mutates the ENCLOSING `count`, no special syntax needed
return count;
}
return increment;
}
let c1 = makeCounter();
let c2 = makeCounter();
output(c1()); // 1
output(c1()); // 2 - c1's own `count` kept its state
output(c2()); // 1 - c2 has an entirely separate `count`
This is the same pattern used for private state in the closures tutorial and the bank-account example under Examples - a closure is Falx's stand-in for "private" fields on an object, alongside actual structs.
Recursion
Functions can call themselves normally. Recursion depth is genuinely tracked and enforced, not just approximated - going past 500 nested calls raises a runtime RecursionDepthExceededException ("Maximum recursion depth of 500 exceeded"), verified by actually recursing past it. Moderate recursion (tens to a few hundred levels) works fine, as in the example programs.
Returning nothing
return; with no value, or falling off the end of a function body, both produce null.