Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Generics

A generic function or type works with many different types. Instead of writing one identity for int and another for string, write one that works for any type:

fn identity(x: T) -> T {
    x
}

let a = identity(5)         // T is int
let b = identity("hello")   // T is string

The T is a type variable — a placeholder that stands in for whatever real type the caller uses. Abra figures out what T should be at each call site.

By convention, type variables are a single uppercase letter, optionally followed by digits: T, U, K, V, T2. Names like Item or Key aren’t type variables — they’d be parsed as ordinary identifiers.

Generic types

Structs and enums can take type parameters too. Put them in angle brackets after the name:

type Pair<T> = {
    first: T
    second: T
}

type Either<A, B> =
    | Left(A)
    | Right(B)

let p = Pair(1, 2)                          // Pair<int>
let e: Either<string, int> = .Left("oops")

Constraints

A generic function can require its type variable to support certain operations. You spell that out with an interface constraint:

fn max(a: T Ord, b: T) -> T {
    if a > b { a } else { b }
}

The T Ord says “any type, as long as it implements Ord”. This lets you use > on a and b inside the body.

You can list multiple constraints:

fn find_and_show(arr: array<T Equal ToString>, target: T) {
    if arr.contains(target) {
        println("found " .. target)
    }
}

You only need to write the constraint once. Other parameters that mention the same T automatically get the same constraints.

Wildcards in type annotations

Sometimes you only want to fix part of a generic type and let the compiler figure out the rest. Use _ for the parts to infer:

use core/map

let m: map<_, string> = map.new()    // key type filled in below
m.insert(1, "hello")
m.insert(2, "world")

Performance

Each combination of type arguments gets its own compiled copy of the function — there’s no runtime cost to using generics. (This is called monomorphization if you want a name for it.)