burn / docs

Language

Enums

def enum declares a type whose values are one of a fixed list of variants.

Plain enums

def enum Color { Red, Green, Blue }

var c = Color.Green
print(c, c == Color.Green, c as int, Color.Red < Color.Blue)

prints Green true 1 true.

  • A variant is written with the enum's name, Color.Green. Inside a match on a Color, the bare name Green works too.
  • Each variant has a number, counting from 0 in the order they are declared. c as int gives it, and variants compare with < and > in that order.
  • n as Color turns a number back into a variant. It is not checked, so only use numbers that came from as int.
  • A plain enum is a number at runtime: it costs nothing to copy or compare, and works as a map key ({Color.Red: "stop"}).
  • It prints as the variant's name, and "${c}" and c as string give that name.
  • The zero value of a plain enum (for a variable or field declared without a value) is its first variant.

Variants with data

A variant can carry fields, written in parentheses like the parameters of a function:

def enum Shape {
    Circle(radius: float),
    Rect(w: float, h: float),
    Dot
}

fun area(s: Shape): float {
    return match s {
        Shape.Circle(r) => 3.14159 * r * r
        Shape.Rect(w, h) => w * h
        Shape.Dot => 0.0
    }
}

var shapes = [Shape.Rect(2.0, 3.0), Shape.Circle(1.0), Shape.Dot]
for s in shapes {
    print(s, area(s))
}
  • A variant with fields is built like a function call: Shape.Rect(2.0, 3.0). A variant without fields is used as it is: Shape.Dot.
  • Values print the way they are built: Shape.Rect(2.0, 3.0), Shape.Dot.
  • == compares the variant and then the fields: Shape.Circle(1.0) == Shape.Circle(1.0) is true.
  • The fields cannot be changed after the value is built. Build a new value instead.
  • Variants with data cannot be converted to numbers with as int.

Reading the fields

match takes a value apart; see variant patterns. To read one variant's fields without a match, check the variant with is, which narrows the value:

def enum Shape {
    Circle(radius: float),
    Dot
}

fun radiusOf(s: Shape): float {
    if (s is Shape.Circle) {
        return s.radius
    }
    return 0.0
}

print(radiusOf(Shape.Circle(2.5)), radiusOf(Shape.Dot))

Shape.Circle is a type here, so s is Shape.Circle, s as? Shape.Circle and is Shape.Circle c in a match all work.

Recursive enums

An enum with data is a reference type, like a struct, so a variant can hold values of its own enum:

def enum Tree {
    Leaf(value: int),
    Node(left: Tree, right: Tree)
}

fun sum(t: Tree): int {
    return match t {
        Tree.Leaf(v) => v
        Tree.Node(l, r) => sum(l) + sum(r)
    }
}

print(sum(Tree.Node(Tree.Leaf(1), Tree.Node(Tree.Leaf(2), Tree.Leaf(3)))))

Adding functions to an enum

An enum body only lists variants. Functions that work on the enum are written outside it, and work as methods through method syntax:

def enum Light { Red, Yellow, Green }

fun next(l: Light): Light {
    return match l {
        Red => Light.Green
        Green => Light.Yellow
        Yellow => Light.Red
    }
}

print(Light.Red.next(), Light.Red.next().next())

Limits

  • Variants cannot be given their own numbers (Red = 2 is an error); they are numbered in order.
  • Enums cannot take type parameters. Use a nullable type instead of an Option, and a generic def type or a struct for the rest.
  • An enum cannot implement an interface; write the functions outside it as shown above.