state machines
===============================================================================
%% A `state machine` models step-by-step behavior as transitions between named states. In Mech, a machine declares its allowed states and output kind, then implements transitions and terminal outputs.
A state machine definition has two parts:
1. A type declaration that lists all legal states and their payload shapes.
2. An implementation that defines the initial state and per-state behavior.
General form:
```text
#MachineName(input<kind>) => <output-kind>
├ :StateA(payload<kind>, ...)
├ :StateB(payload<kind>, ...)
└ :Done(result<kind>).
#MachineName(input<kind>) -> :StateA(...)
:StateA(...) => :StateB(...)
:StateB(...)
├ guard -> :StateA(...)
└ guard -> :Done(...)
:Done(result) => result.
```
Notes:
- `#Name(...) => <...>` declares the machine interface and the full state set.
- Every transition target must be one of the declared states.
- Integer kinds are strict in machine transitions, so typed literals like `0u64`
are often required.
- A state arm can be a direct transition (`->`) or guarded branches.
- Terminal states return with `=>`.
1. Traffic light
-------------------------------------------------------------------------------
A traffic light cycles through three phases. This is the canonical example of a
state machine: behavior is entirely determined by the current state, not by a
counter or accumulator.
```mech:ex state-machine-traffic-light
#TrafficLight(steps<u64>) => <u64>
├ :Red(steps<u64>)
├ :Green(steps<u64>)
├ :Yellow(steps<u64>)
└ :Done(out<u64>).
#TrafficLight(steps<u64>) -> :Red(steps)
:Red(steps)
├ steps > 0u64 -> :Green(steps - 1u64)
└ steps == 0u64 -> :Done(0u64)
:Green(steps)
├ steps > 0u64 -> :Yellow(steps - 1u64)
└ steps == 0u64 -> :Done(0u64)
:Yellow(steps)
├ steps > 0u64 -> :Red(steps - 1u64)
└ steps == 0u64 -> :Done(0u64)
:Done(out) => out.
#TrafficLight(6u64)
```
Expected result: `0u64` after cycling Red → Green → Yellow twice.
2. Fibonacci machine
-------------------------------------------------------------------------------
State payload can carry multiple values. `:Compute` stores the loop index and
two rolling values, making the recurrence relation explicit in the transitions.
```mech:ex state-machine-fibonacci
#Fibonacci(n<u64>) => <u64>
├ :Compute(n<u64>, a<u64>, b<u64>)
└ :Done(n<u64>).
#Fibonacci(n<u64>) -> :Compute(n, 0u64, 1u64)
:Compute(n, a, b)
├ n > 0u64 -> :Compute(n - 1u64, b, a + b)
└ n == 0u64 -> :Done(a)
:Done(n) => n.
#Fibonacci(10u64)
```
Expected result: `55u64`.
3. Type strictness in guards and transitions
-------------------------------------------------------------------------------
State payload kinds are strict. If a state carries `u64`, untyped numeric
literals in guards or transitions will fail typechecking.
```mech:disabled
#Clamp(n<u64>, lo<u64>, hi<u64>) => <u64>
├ :Check(n<u64>, lo<u64>, hi<u64>)
└ :Done(out<u64>).
#Clamp(n<u64>, lo<u64>, hi<u64>) -> :Check(n, lo, hi)
:Check(n, lo, hi)
├ n < lo -> :Done(lo)
├ n > hi -> :Done(hi)
└ n -> :Done(n) %% guards must match declared kind
:Done(out) => out.
```
Write typed literals so guard expressions stay consistent with the state
declaration:
```mech:ex state-machine-clamp
#Clamp(n<u64>, lo<u64>, hi<u64>) => <u64>
├ :Check(n<u64>, lo<u64>, hi<u64>)
└ :Done(out<u64>).
#Clamp(n<u64>, lo<u64>, hi<u64>) -> :Check(n, lo, hi)
:Check(n, lo, hi)
├ n < lo -> :Done(lo)
├ n > hi -> :Done(hi)
└ n >= lo & n <= hi -> :Done(n)
:Done(out) => out.
#Clamp(15u64, 1u64, 10u64)
```
Expected result: `10u64`.
4. Declared states must be implemented consistently
-------------------------------------------------------------------------------
Transitions to undeclared states are invalid:
```mech:disabled
#Turnstile(n<u64>) => <u64>
├ :Locked(n<u64>)
└ :Unlocked(n<u64>).
#Turnstile(n<u64>) -> :Locked(n)
:Locked(n) -> :Unlocked(n)
:Unlocked(n) -> :Spinning(n) -- :Spinning was never declared
```
Declare and implement every state the machine can enter:
```mech:ex state-machine-turnstile
#Turnstile(coins<u64>) => <u64>
├ :Locked(coins<u64>)
├ :Unlocked(coins<u64>)
├ :Spinning(coins<u64>)
└ :Done(out<u64>).
#Turnstile(coins<u64>) -> :Locked(coins)
:Locked(coins)
├ coins > 0u64 -> :Unlocked(coins - 1u64)
└ coins == 0u64 -> :Done(0u64)
:Unlocked(coins) -> :Spinning(coins)
:Spinning(coins) -> :Locked(coins)
:Done(out) => out.
#Turnstile(2u64)
```
Expected result: `0u64` after inserting two coins and spinning twice.