Functions
Functions are the primary way to organize and reuse code in Zeus.
Basic Syntax
function functionName(param1: Type1, param2: Type2): ReturnType { // function body return value;}Simple Functions
A function that takes two integers and returns their sum:
function add(a: i32, b: i32): i32 { return a + b;}
function main(): i32 { let result = add(10, 20); return result; // Returns 30}Parameters
Parameters are listed with their types:
function greet(times: i32): i32 { return times;}
function calculate(x: f64, y: f64, z: f64): f64 { return x + y + z;}Rest Parameters
A rest parameter collects a variable number of trailing arguments into an array. Prefix the final parameter with ... and give it an array type:
function sum(...nums: i32[]): i32 { let total: i32 = 0; for (let i: i32 = 0; i < nums.length; i++) { total += nums[i]; } return total;}
function main(): i32 { return sum(1, 2, 3, 4, 5); // 15}Inside the function the rest parameter is an ordinary array, so .length, indexing, and the other array methods are all available. Each argument you pass is type-checked against the array’s element type.
A rest parameter can follow any number of fixed parameters. The fixed arguments are matched first, and everything after them is collected:
function sumFrom(base: i32, ...rest: i32[]): i32 { let total: i32 = base; for (let i: i32 = 0; i < rest.length; i++) { total += rest[i]; } return total;}
function main(): i32 { return sumFrom(100, 1, 2, 3); // 106}Calling with no trailing arguments produces an empty array:
function count(...items: i32[]): i32 { return items.length;}
function main(): i32 { return count(); // 0}Rest parameters work everywhere functions do — including class methods, anonymous functions, fat arrows, and function references. For example, calling a variadic function through a function-typed value:
function sum(...nums: i32[]): i32 { let total: i32 = 0; for (let i: i32 = 0; i < nums.length; i++) { total += nums[i]; } return total;}
function main(): i32 { let f: (...nums: i32[]) => i32 = sum; return f(2, 4, 6); // 12}Return Types
The return type is specified after the parameter list:
function getNumber(): i32 { return 42;}
function isPositive(n: i32): boolean { return n > 0;}
function getPi(): f64 { return 3.14159;}Void Functions
Functions that don’t return a value can omit the return type annotation entirely — it defaults to void. You can also write : void explicitly if you prefer to be clear:
function doSomething() { let x = 10; // No return statement needed}
function process(value: i32) { let doubled = value * 2; return; // Optional explicit return}
// Explicit : void is also valid and means the same thingfunction reset(): void { // ...}The main Function
The main function is optional in Zeus. If defined, it serves as the program entry point and its return value becomes the exit code:
function main(): i32 { // Runs after all module-level code return 0; // Exit code}If no main function is defined, the program exits with code 0 after running all module-level code.
Calling Functions
Call functions by name with arguments in parentheses:
function multiply(a: i32, b: i32): i32 { return a * b;}
function main(): i32 { let x = multiply(6, 7); // x = 42 let y = multiply(x, 2); // y = 84 let z = multiply(multiply(2, 3), 4); // z = 24
return z;}Recursion
Functions can call themselves:
function factorial(n: i32): i32 { if (n <= 1) { return 1; } return n * factorial(n - 1);}
function main(): i32 { return factorial(5); // Returns 120}Functions with Objects
Functions can take and return objects:
class Point { public x: i32; public y: i32;
constructor(x: i32, y: i32) { this.x = x; this.y = y; }}
function createPoint(x: i32, y: i32): Point { return new Point(x, y);}
function addPoints(p1: Point, p2: Point): Point { return new Point(p1.x + p2.x, p1.y + p2.y);}
function main(): i32 { let a = createPoint(1, 2); let b = createPoint(3, 4); let c = addPoints(a, b);
return c.x + c.y; // Returns 10}Multiple Return Paths
All code paths must return a value (for non-void functions):
function abs(n: i32): i32 { if (n < 0) { return n * -1; } else { return n; }}
function max(a: i32, b: i32): i32 { if (a > b) { return a; } return b; // Implicit else}Function Types
A function’s type describes its parameter types and return type. The syntax mirrors TypeScript:
(param: Type, ...) => ReturnTypeFunction types are used to annotate variables and parameters that hold functions:
// Variable annotated with a function typelet op: (a: i32, b: i32) => i32;
// Parameter annotated with a function typefunction apply(f: (x: i32) => i32, val: i32): i32 { return f(val);}The parameter names in a function type are optional — they’re for readability only. Both of these mean the same thing:
let op: (a: i32, b: i32) => i32; // with nameslet op: (i32, i32) => i32; // without namesFunction types can also appear as return type annotations:
// A function that returns another functionfunction makeAdder(n: i32): (x: i32) => i32 { return (x: i32): i32 => { return x + n; }}Function References
Named top-level functions can be stored in variables and passed as arguments. Annotate the variable with a function type:
function add(a: i32, b: i32): i32 { return a + b;}
function subtract(a: i32, b: i32): i32 { return a - b;}
function apply(f: (a: i32, b: i32) => i32, x: i32, y: i32): i32 { return f(x, y);}
function main(): i32 { let op: (a: i32, b: i32) => i32 = add; let result1 = op(10, 5); // 15
op = subtract; let result2 = op(10, 5); // 5
let result3 = apply(add, 3, 7); // 10
return result1 + result2 + result3; // 30}Anonymous Functions
An anonymous function is a function expression without a top-level name. Assign it to a variable or pass it directly as an argument:
function main(): i32 { // Assigned to a variable let double = function(x: i32): i32 { return x * 2; }; let result = double(5); // 10
return result;}You can also give an anonymous function an internal name for recursion. The name is visible both inside the function body and in the enclosing scope:
function main(): i32 { let fib = function fibonacci(n: i32): i32 { if (n <= 1) { return n; } return fibonacci(n - 1) + fibonacci(n - 2); } return fib(8); // 21}Anonymous functions can be passed directly as arguments:
function apply(f: (x: i32) => i32, val: i32): i32 { return f(val);}
function main(): i32 { return apply(function(x: i32): i32 { return x * 3; }, 7); // 21}Fat Arrow Functions
Fat arrow syntax (=>) is a compact alternative to function for writing anonymous functions. The body must be a block:
// Named function equivalent: function(x: i32): i32 { return x * 2; }let double = (x: i32): i32 => { return x * 2; }
// Void (no return type annotation = void, same as regular functions)let log_value = (x: i32) => { log(x); }Fat arrows are especially useful for inline callbacks:
function apply(f: (x: i32) => i32, val: i32): i32 { return f(val);}
function main(): i32 { let result = apply((x: i32): i32 => { return x + 1; }, 9); return result; // 10}Return type annotation comes between ) and =>:
(param: Type, ...): ReturnType => { body }| Syntax | Return type |
|---|---|
(x: i32): i32 => { return x; } | i32 |
(x: i32) => { log(x); } | void (default) |
Immediately Invoked Function Expressions (IIFE)
A fat arrow function can be called immediately after it is defined by appending (args). This creates and calls the function in a single expression:
function main(): i32 { let result = (a: i32, b: i32): i32 => { return a + b; }(3, 7);
return result; // 10}IIFEs are useful for one-off computations you want to keep scoped:
function main(): i32 { // Compute a value using local variables that don't pollute the outer scope let offset = (): i32 => { let base = 100; let adjustment = 42; return base - adjustment; }();
return offset; // 58}