← All Examples ADTs
State Machine
A traffic light modeled as a state machine using algebraic data types. Exhaustive pattern matching ensures every state and action is handled.
Source Code
type Light =
| Red
| Yellow
| Green
type Action =
| Next
| Emergency
fn transition(light: Light, action: Action) -> Light {
match action {
Emergency => Red
Next => match light {
Red => Green
Green => Yellow
Yellow => Red
}
}
}
fn light_name(light: Light) -> String {
match light {
Red => "RED"
Yellow => "YELLOW"
Green => "GREEN"
}
}
fn run_sequence(light: Light, steps: Int) -> Light {
if steps <= 0 { light }
else {
println(" " <> light_name(light))
run_sequence(transition(light, Next), steps - 1)
}
}
fn main() {
println("Traffic light sequence:")
let final_state = run_sequence(Red, 7)
println("Final state: " <> light_name(final_state))
println("Emergency from Green:")
let emergency = transition(Green, Emergency)
println(" " <> light_name(emergency))
} What This Demonstrates
- Algebraic data types --
LightandActionare sum types with named variants. No classes, no inheritance. - Exhaustive pattern matching -- The compiler verifies every variant is handled. Missing a case is a compile error.
- Nested matching --
matchexpressions can be nested inside othermatcharms. - Recursive state transitions --
run_sequenceuses recursion to step through states, a natural fit for functional programming. - String concatenation -- The
<>operator joins strings.
Line-by-Line Breakdown
type Light = | Red | Yellow | Green- Defines a sum type with three variants. Each variant is a distinct value of type
Light. type Action = | Next | Emergency- A second sum type representing possible inputs to the state machine.
fn transition(light: Light, action: Action) -> Light- A pure function that computes the next state. No mutation -- it returns a new
Lightvalue. Emergency => Red- Regardless of the current state, an emergency action always transitions to
Red. Next => match light { ... }- Normal transitions cycle through Green, Yellow, Red. The nested match handles each current state.
fn run_sequence(light: Light, steps: Int) -> Light- Recursively applies
Nexttransitions, printing each state. Terminates whenstepsreaches zero.
Try Modifying
- Add a
FlashingRedvariant and aMalfunctionaction that transitions to it. - Add a duration to each state -- define
type TimedLight = | Timed(Light, Int)and adjust transitions. - Create a second state machine (e.g., a door:
Open | Closed | Locked) using the same pattern.