Albite

Data types

Sum types

A sum type lists alternatives, each of which may carry a payload:

type Option<T> = None | Some(T)

type Result<T, E> = Ok(T) | Err(E)

type List<T> = Nil | Cons(T, List<T>)

type Tree<T> = Leaf | Node(T, Tree<T>, Tree<T>)

They may be written across several lines with a leading |, which reads better once there are more than two:

type Shape =
    | Circle(Nat)
    | Rect(Nat, Nat)

A single-constructor type needs no leading bar:

type Pair<A, B> = MkPair(A, B)

Note that List and Tree above refer to themselves. Recursive types are ordinary types here, and the compiler handles the heap allocation. Two rules apply to them: a type may not refer to itself to the left of an arrow (see Strictly positive types), and a type whose recursion passes through another generic type — Node(List<Tree>) — cannot be matched on (see A recursive type nested inside a generic one).

Take values apart with match.

Records

A record has named fields:

type Point = { x: Nat, y: Nat }

type Span = { start: Nat, end: Nat, file: String }

Construct one with braces and read a field with a dot:

fn make_point(x: Nat, y: Nat) -> Point {
    { x: x, y: y }
}

fn get_x(p: Point) -> Nat {
    p.x
}

Named constructors

Since 1.5

Prefixing the braces with the type name says which record you mean, which matters when the expected type is not otherwise obvious:

fn origin() -> Point {
    Point { x: 0, y: 0 }
}

Record spread

Since 1.5

Build a record from an existing one, overriding selected fields:

fn shift(p: Point, dx: Nat) -> Point {
    { ...p, x: p.x + dx }
}

The spread comes first, and later fields win. This is a functional update — p is unchanged.

Type aliases

An alias is a second name for an existing type:

pub type Char = Nat
pub type SourceEntry = (String, String)

Aliases may take type parameters — see Generics.

Choosing between them

Reach for a sum type when a value is one of several shapes, and let exhaustiveness checking make sure every shape is handled. Reach for a record when a value has several independent parts that are all present at once.

A tuple is for when the parts are not worth naming. Note that tuples have no index access — there is no p.0; you get at the parts by destructuring, which means every use site names them anyway. Once that naming starts feeling repetitive, it is a sign the value wants to be a record.