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

Enums

An enum is a type whose value is one of a fixed set of variants. Use them when a value can be one of a few distinct things — a color, a direction, the result of a network request, the kind of token a parser just read.

type Color =
    | Red
    | Green
    | Blue

Construct a variant by qualifying it with the enum name:

let c = Color.Red

When Abra can infer the enum type from context — a type annotation, a function parameter, a comparison against another value of the same enum — you can drop the prefix and write just the variant with a leading dot:

let c: Color = .Red

fn paint(c: Color) { ... }
paint(.Green)

If the type can’t be inferred at the call site, the leading-dot form won’t compile and you’ll need to fully qualify the variant.

Variants with data

Each variant can carry its own data. Construct one by passing the data as arguments, the same way you’d construct a struct:

type Shape =
    | Circle(float)
    | Rectangle(float, float)
    | Origin

let c = Shape.Circle(5.0)
let r = Shape.Rectangle(2.0, 4.0)
let o = Shape.Origin

Or with leading-dot syntax when the type is known from context:

fn area(s: Shape) -> float { ... }

area(.Circle(5.0))
area(.Rectangle(2.0, 4.0))
area(.Origin)

Named fields and defaults

A variant’s fields can be named, and named fields can have default values:

type Color =
    | Rgb(red: int = 0, green: int = 0, blue: int = 0)
    | Named(string)

let red    = Color.Rgb(red = 255)
let yellow = Color.Rgb(red = 255, green = 255)
let teal   = Color.Rgb(green = 128, blue = 128)

At a call site, named arguments may appear in any order, but must come after any positional ones. If every field has a default, you can call the constructor with no arguments at all (Color.Rgb()).

A variant’s fields must be either all named or all unnamed — you can’t mix the two within a single variant.

Handling all the variants

Use match to do something different for each case. The compiler checks that you’ve covered every variant, so you can’t forget one:

let pi = 3.14159

fn area(s: Shape) -> float {
    match s {
        .Circle(r) -> pi * r * r
        .Rectangle(w, h) -> w * h
        .Origin -> 0.0
    }
}

If you later add a new variant to Shape, every match on Shape will start failing to compile until you handle it. That’s exactly what you want.

Generic enums

Enums can take type parameters too. The standard library uses this for option and result:

type option<T> =
    | some(T)
    | none

type result<T, E> =
    | ok(T)
    | err(E)

These two come up everywhere — see Error Handling.