← 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

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 Light value.
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 Next transitions, printing each state. Terminates when steps reaches zero.

Try Modifying