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;}
let result = add(10, 20);console.log(result); // 30Parameters
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;}Default Parameters
A parameter can specify a default value with = expression. When the caller omits that argument, the default is used in its place:
function greet(name: string, greeting: string = "Hello"): string { return greeting + ", " + name;}
console.log(greet("Zeus")); // "Hello, Zeus"console.log(greet("Zeus", "Hi")); // "Hi, Zeus" — default overriddenOnly trailing parameters may have defaults: once a parameter has one, every parameter after it must have one too (a rest parameter is the sole exception, since it is always optional).
function box(width: i32, height: i32 = 10, depth: i32 = 1): i32 { return width * height * depth;}
console.log(box(2)); // 2 * 10 * 1 = 20console.log(box(2, 3)); // 2 * 3 * 1 = 6console.log(box(2, 3, 4)); // 2 * 3 * 4 = 24Defaults are not limited to literals — any expression works, and it is evaluated at the call site each time the argument is omitted.
Default parameters work on free functions, class methods, static methods, and constructors:
class Counter { public count: i32;
constructor(start: i32 = 0) { this.count = start; }
add(by: i32 = 1): i32 { this.count += by; return this.count; }}
let c = new Counter(); // start defaults to 0c.add(); // by defaults to 1 → count = 1c.add(5); // count = 6console.log(c.count); // 6Rest 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;}
console.log(sum(1, 2, 3, 4, 5)); // 15Inside 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;}
console.log(sumFrom(100, 1, 2, 3)); // 106Calling with no trailing arguments produces an empty array:
function count(...items: i32[]): i32 { return items.length;}
console.log(count()); // 0Rest 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;}
let f: (...nums: i32[]) => i32 = sum;console.log(f(2, 4, 6)); // 12Return 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;}
let x = multiply(6, 7); // x = 42let y = multiply(x, 2); // y = 84let z = multiply(multiply(2, 3), 4); // z = 24
console.log(z); // 24Recursion
Functions can call themselves:
function factorial(n: i32): i32 { if (n <= 1) { return 1; } return n * factorial(n - 1);}
console.log(factorial(5)); // 120Functions 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);}
let a = createPoint(1, 2);let b = createPoint(3, 4);let c = addPoints(a, b);
console.log(c.x + c.y); // 10Multiple 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);}
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
console.log(result1 + result2 + result3); // 30Anonymous 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:
// Assigned to a variablelet double = function(x: i32): i32 { return x * 2; };let result = double(5); // 10
console.log(result); // 10You 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:
let fib = function fibonacci(n: i32): i32 { if (n <= 1) { return n; } return fibonacci(n - 1) + fibonacci(n - 2);}console.log(fib(8)); // 21Anonymous functions can be passed directly as arguments:
function apply(f: (x: i32) => i32, val: i32): i32 { return f(val);}
console.log(apply(function(x: i32): i32 { return x * 3; }, 7)); // 21Fat 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 noop = (x: i32) => { let unused: i32 = x; }Fat arrows are especially useful for inline callbacks:
function apply(f: (x: i32) => i32, val: i32): i32 { return f(val);}
let result = apply((x: i32): i32 => { return x + 1; }, 9);console.log(result); // 10Return type annotation comes between ) and =>:
(param: Type, ...): ReturnType => { body }| Syntax | Return type |
|---|---|
(x: i32): i32 => { return x; } | i32 |
(x: i32) => { /* no return */ } | 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:
let result = (a: i32, b: i32): i32 => { return a + b; }(3, 7);
console.log(result); // 10IIFEs are useful for one-off computations you want to keep scoped:
// Compute a value using local variables that don't pollute the outer scopelet offset = (): i32 => { let base = 100; let adjustment = 42; return base - adjustment;}();
console.log(offset); // 58