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;
}
let result = add(10, 20);
console.log(result); // 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;
}

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 overridden

Only 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 = 20
console.log(box(2, 3)); // 2 * 3 * 1 = 6
console.log(box(2, 3, 4)); // 2 * 3 * 4 = 24

Defaults 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 0
c.add(); // by defaults to 1 → count = 1
c.add(5); // count = 6
console.log(c.count); // 6

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;
}
console.log(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;
}
console.log(sumFrom(100, 1, 2, 3)); // 106

Calling with no trailing arguments produces an empty array:

function count(...items: i32[]): i32 {
return items.length;
}
console.log(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;
}
let f: (...nums: i32[]) => i32 = sum;
console.log(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;
}
let x = multiply(6, 7); // x = 42
let y = multiply(x, 2); // y = 84
let z = multiply(multiply(2, 3), 4); // z = 24
console.log(z); // 24

Recursion

Functions can call themselves:

function factorial(n: i32): i32 {
if (n <= 1) {
return 1;
}
return n * factorial(n - 1);
}
console.log(factorial(5)); // 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);
}
let a = createPoint(1, 2);
let b = createPoint(3, 4);
let c = addPoints(a, b);
console.log(c.x + c.y); // 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);
}
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); // 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:

// Assigned to a variable
let double = function(x: i32): i32 { return x * 2; };
let result = double(5); // 10
console.log(result); // 10

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:

let fib = function fibonacci(n: i32): i32 {
if (n <= 1) { return n; }
return fibonacci(n - 1) + fibonacci(n - 2);
}
console.log(fib(8)); // 21

Anonymous 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)); // 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 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); // 10

Return type annotation comes between ) and =>:

(param: Type, ...): ReturnType => { body }
SyntaxReturn 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); // 10

IIFEs are useful for one-off computations you want to keep scoped:

// 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;
}();
console.log(offset); // 58