Skip to content

Generics

Type parameters, inference, explicit arguments, bounds, specialization, and opaque types.

Updated View as Markdown

Generics let one declaration operate over a family of types while preserving static checking.

fn identity[T](value: T): T {
    return value
}

Type parameters use square brackets.

Types

Structs, enums, aliases, traits, and functions can declare type parameters.

struct Pair[A, B] {
    first: A
    second: B
}

enum Tree[T] {
    Leaf(T)
    Branch([]Tree[T])
}

type Predicate[T] = fn(T) -> bool

Each use supplies the declared number of arguments.

entry: Pair[string, int] = Pair {
    first: "port"
    second: 8080
}

Different arguments create different static instantiations. Pair[int, bool] does not share a type with Pair[float, string].

Inference

Calls normally infer type arguments from their parameters and expected result.

number := identity(42)
text := identity("atoll")

Each call gets an independent substitution. A prior call with int does not permanently specialize the declaration for later callers.

Explicit calls

When inference cannot recover an argument, place type arguments in square brackets before the call parentheses.

size := size_of[Packet]()
empty := List.new[string]()
converted := source.convert[Target]()

Without following (...), value[index] remains an indexing expression. The call shape makes the generic argument list unambiguous.

The old ::<T> and <T> call spellings are not the current convention.

Bounds

A bound lists behavior required from a type parameter.

fn larger[T](left: T, right: T): T
where T: Comparable[T] {
    if left > right { left } else { right }
}

+ combines requirements.

fn render[T](value: T): string
where T: Display + Clone {
    return value.to_string()
}

The checker resolves calls and operators inside the generic body through those bounds. Missing behavior is reported at the use that requires it.

Bounds may also appear inline where the declaration grammar permits. A where clause is clearer when several parameters or requirements interact.

Self

Inside a trait, Self denotes the implementing type.

trait Merge {
    fn merge(self, other: Self): Self
}

Inside an implementation, declared type parameters and Self are substituted with the implementation’s concrete target.

Blanket implementations

An implementation can itself be generic.

trait Printable: Display + Debug {}

impl[T] Printable for T
where T: Display + Debug {}

The coherence checker rejects implementations that overlap without a provable disjoint constraint. Generic convenience cannot make method selection ambiguous.

Opaque types

impl Trait hides a concrete type behind a trait contract.

fn area(shape: impl Shape): float {
    return shape.area()
}

In parameter position it accepts a concrete argument satisfying the trait. In return position, a declaration produces one hidden concrete type anchored to that function.

Two functions returning impl Trait do not automatically expose the same opaque type even if their current bodies choose the same concrete representation.

Atoll currently uses static dispatch. There is no dyn Trait type or user-visible vtable contract.

Monomorphization

The compiler specializes generic functions, methods, closures, and layouts for reachable concrete arguments. This supports direct calls and concrete field layouts.

Monomorphization is an implementation strategy, not permission to inspect a type parameter without a bound. Generic source is checked as generic source before specialization.

Recursive generics

Recursive uses must still have a finite representation.

enum Node[T] {
    Empty
    Children([]Node[T])
}

The List introduces managed indirection. A payload containing Node[T] directly with no representational break is rejected.

Type matching

The current compiler has a compile-time type match used by specialized generic code.

fn width_code[T](): int {
    return match T {
        u8 => 1
        u16 => 2
        else => 0
    }
}

This is compile-time specialization over T, not a runtime value is Type test.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close