Enums
Say that a value is one of a fixed set of cases, carry data with each case, and let match check you've handled them all.
Some values can only be one of a few things: a traffic light is red, amber or green; a download is waiting, running, done or failed. An enum lists those cases, and the compiler makes sure every part of your program handles all of them.
enum Light (
Red
Amber
Green
)
Light now = Light.Red
log now == Light.Red
log now == Light.GreenWrite each case on its own line. Refer to a case through the enum's name: Light.Red.
match checks every case#
match on an enum must cover every case, so you can't forget one:
enum Light (
Red
Amber
Green
)
def next(Light l) Light -> match l (
Light.Red -> Light.Green
Light.Green -> Light.Amber
Light.Amber -> Light.Red
)
def say(Light l) string -> match l (
Light.Red -> "stop"
Light.Amber -> "wait"
Light.Green -> "go"
)
Light l = Light.Red
loop 4 (
log say(l)
l = next(l)
)Add a fourth case to Light and both functions stop compiling until you handle it:
enum Light (
Red
Amber
Green
FlashingAmber
)
def say(Light l) string -> match l (
Light.Red -> "stop"
Light.Amber -> "wait"
Light.Green -> "go"
)That's the point of an enum: when the set of cases changes, the compiler finds every place that needs updating.
Cases that carry data#
A case can hold a value, written in brackets after its name. Each case can hold a different type, or nothing at all:
enum Shape (
Circle(number)
Square(number)
Dot
)
def area(Shape s) number -> match s (
Shape.Circle(r) -> 3.14 * r * r
Shape.Square(side) -> side * side
Shape.Dot -> 0
)
Shape[] shapes = [Shape.Circle(2), Shape.Square(3), Shape.Dot]
for s in shapes (
log area(s)
)In Shape.Circle(r) -> ..., the name r takes the value the circle was made with, so the arm can use it.
A real example: loading#
Enums with data describe states well. A value can't be "loaded" and "failed" at once, and the data that goes with each state only exists in that state:
enum Load (
Waiting
Done(string)
Failed(string)
)
def show(Load state) string -> match state (
Load.Waiting -> "loading…"
Load.Done(text) -> "got " ++ text.len ++ " characters"
Load.Failed(reason) -> "failed: " ++ reason
)
log show(Load.Waiting)
log show(Load.Done("hello world"))
log show(Load.Failed("timed out"))switch on an enum#
switch takes enum cases too, when each case should run code rather than produce a value. Inside the case, the cases are written without the enum name:
enum Command (
Move(int)
Turn(string)
Stop
)
Command[] program = [Command.Move(3), Command.Turn("left"), Command.Move(1), Command.Stop]
for c in program (
switch c (
case Move(steps)
log "move " ++ steps
case Turn(direction)
log "turn " ++ direction
case Stop
log "stop"
)
)Why is this better than storing the state as a string like "waiting" or "done"?
Coin values
Finish cents so it returns each coin's value: a penny is 1, a nickel 5, a dime 10 and a quarter 25. Then the program prints the total of the purse.
66