Generics

Zith supports generic programming with both explicit and implicit type parameters, constrained via traits or type unions using the or keyword.

Explicit Generics

fn identity<T>(val: T): T { val }

struct Pair<T, U> {
    first: T,
    second: U,
}

fn serialize<T: Serializable + Printable, U: Clone>(val: T, ctx: U): string { ... }

Implicit Generics

When type parameters are omitted, the compiler infers constraints from usage at the call site:

fn add(a, b) { a + b }     // Arithmetic implicitly required
fn print(val) { @println(val); }  // Printable implicitly required

Type Constraints with or

The or keyword specifies compile-time type constraints — a union of acceptable types dispatched at compile time:

type Number = i32 or f64 or bool;

fn convert<T: Number>(val: T): string {
    // T must be one of i32, f64, or bool
    // Specialised code generated for each
}

Contrast with union, where variants are separated by commas and dispatch happens at runtime (see Type System).

Generic Implementations

implement Pair<T, U> {
    fn swap(self: lend) {
        let tmp = self.first;
        self.first = self.second;
        self.second = tmp;
    }
}

implement Vec<T> as Printable {
    fn print(self) {
        for (v in self) { @println(v); }
    }
}

Monomorphisation

Zith compiles generics via monomorphisation: each unique combination of type arguments generates a specialised copy of the function or struct. This ensures zero-cost abstractions with no runtime dispatch overhead for generic code.

Where to Use Generics

See also: Spec: Polymorphism


Next: Memory Model — NRA