Skip to content

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 thing
function 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, ...) => ReturnType

Function types are used to annotate variables and parameters that hold functions:

// Variable annotated with a function type
let op: (a: i32, b: i32) => i32;
// Parameter annotated with a function type
function 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 names
let op: (i32, i32) => i32; // without names

Function types can also appear as return type annotations:

// A function that returns another function
function 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 }
SyntaxReturn 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
}