Enums
An enum is a top-level list of named values, one member per line:
enum Color do
RED
GREEN
BLUE
end
Color current = Color.GREEN
print(current == Color.GREEN, current) // true GREENMembers are written as Enum.MEMBER. Enum values can be stored in variables,
fields, and arrays, passed to and returned from functions, and compared with
== and !=. Values of different enums are distinct types, and enums do not
support arithmetic or ordering. print shows the member name.
Positions and values
Each member has a position, from 0 in the order written, and a value, which is its position unless one is written:
enum Status do
OK = 200
MOVED = 301
MISSING = 404
end| Expression | Result |
|---|---|
ordinal(member) |
its position, an int |
int(member) |
its value; i64(member) and the other integer conversions too |
Kind.from_ordinal(i) |
the member at position i, as an Option[Kind]: NONE when there is none |
len(Kind) |
the number of members |
An array can be indexed by a member, which stands for its position, so a table with a value per member is an array:
enum Kind do
STONE
WOOD
METAL
end
int[] points = [5, 10, 25]
print(points[Kind.WOOD]) // 10
for i = 0 to len(Kind) - 1 do
match Kind.from_ordinal(i) do
when SOME(kind) then
print(kind, points[kind])
when NONE then
end
endA written value is an integer that fits an i32, as C's enums do, and no
two members may have the same value. Members that carry values cannot have numbers.
Flags
An enum marked #[flags] is a set of flags: its members' values are 1, 2,
4, and so on, unless written, and |, &, ^, and ~ combine them into
values of the same enum. flag in flags says whether flags holds all of
flag's bits, and print shows the members a value holds, joined with
|:
#[flags]
enum Permission do
READ
WRITE
EXEC
ALL = 7
end
Permission p = Permission.READ | Permission.WRITE
print(p, Permission.WRITE in p, Permission.EXEC in p) // READ|WRITE true falseMembers that carry values
A member can carry values, declared like function parameters. Such a member is written with its values in parentheses; a member without values is written as before:
enum Shape do
CIRCLE(f64 radius)
RECT(f64 width, f64 height)
EMPTY
end
Shape[] shapes = [Shape.CIRCLE(1.0), Shape.RECT(2.0, 3.0), Shape.EMPTY]A value of such an enum is always exactly one member with its values, so a
Shape is never null. The values are read with match, which
gives them names, and which must handle every member:
func area(Shape s) f64 do
match s do
when CIRCLE(r) then
return 3.14159 * r * r
when RECT(w, h) then
return w * h
when EMPTY then
return 0.0
end
endLeaving out EMPTY there is an error, Match does not handle Shape.EMPTY,
and so is every match that misses a member added later: the compiler finds
each place that must change. That is what such an enum is for. Other
languages call it a tagged union, a sum type, or, in Rust and Swift, an enum
too.
An interface also gives one type to values of several kinds, but it leaves the kinds open: any type with its methods is one, in any module, and code calls the methods without knowing which kind it has. An enum with values closes them: its members are all the kinds there are, in one place, and code looks at each. Use an enum when the kinds are fixed and the code that uses them decides what each means, as the tokens of a parser or the results of a request; use an interface when new kinds come from elsewhere, as shapes a program's users add.
A member may carry the enum itself, which describes trees and lists:
enum Expr do
NUMBER(int value)
ADD(Expr left, Expr right)
endThese values are references, like structs: copying one copies the reference.
print and to_str show the member name, and == is not available for them;
use match.
Type parameters
An enum can take type parameters in brackets after its name. Its members use them like types, and each use of the enum gives them types:
enum Tree[T] do
LEAF(T value)
NODE(Tree[T] left, Tree[T] right)
end
Tree[str] tree = Tree.NODE(Tree.LEAF("a"), Tree.LEAF("b"))Tree[str] and Tree[int] are different types. A value such as
Tree.LEAF("a") takes its type arguments from where it goes, such as the
declared type of a variable, parameter, field, array, or function result, and
otherwise from the values it carries. When neither tells, as in
print(Option.NONE) on its own, the compiler asks for a declared type.
Structs take type parameters the same way, and so do
functions, as generic functions.