Hierarchical Machines
What are Hierarchical Machines?
Section titled “What are Hierarchical Machines?”Hierarchical State Machines (HSMs) allow you to build complex state machines by nesting child machines within parent states. Matchina’s approach flattens nested definitions into a single-level machine with dot-notation keys.
Key benefits:
- Modular definition - Define nested structure, run as flat machine
- Simple runtime - No nested machine instances to manage
- Familiar events - Same
send()API, states like"Working.Red"
When to Use Hierarchy
Section titled “When to Use Hierarchy”Good examples (temporary delegation):
- Traffic light -
WorkingcontainsRed/Green/Yellowcycle - Checkout flow -
PaymentcontainsMethodEntry/Authorizing/Authorized - Search bar -
Activeowns focus, containsTextEntry/Results
Bad examples (semantic grouping):
- “Loading” nested under “Viewing” when both can exist independently
- Using hierarchy as namespacing for unrelated concerns
Flattening Approach
Section titled “Flattening Approach”Define nested structure with defineSubmachine, then flatten to a single-level machine:
import { defineMachine, defineSubmachine, flattenMachineDefinition, createMachineFromFlat, defineStates} from "matchina";
// Define hierarchical structureconst def = defineMachine( defineStates({ Broken: undefined, Working: defineSubmachine( defineStates({ Red: undefined, Green: undefined, Yellow: undefined }), { Red: { tick: "Green" }, Green: { tick: "Yellow" }, Yellow: { tick: "Red" }, }, "Red" ), Maintenance: undefined, }), { Broken: { repair: "Working", maintenance: "Maintenance" }, Working: { break: "Broken", maintenance: "Maintenance" }, Maintenance: { complete: "Working" }, }, "Working");
// Flatten and create machineconst flat = flattenMachineDefinition(def);const machine = createMachineFromFlat(flat);
// States are now: "Broken", "Working.Red", "Working.Green", "Working.Yellow", "Maintenance"machine.getState().key; // "Working.Red" (initial)machine.send("tick"); // -> "Working.Green"machine.send("break"); // -> "Broken"machine.send("repair"); // -> "Working.Red"How Flattening Works
Section titled “How Flattening Works”State Keys
Section titled “State Keys”Nested states become dot-notation keys:
Workingwith childRed→"Working.Red"Workingwith childGreen→"Working.Green"
Transitions
Section titled “Transitions”- Child transitions are prefixed:
Red: { tick: "Green" }→"Working.Red": { tick: "Working.Green" } - Parent transitions apply to all child leaves:
Working: { break: "Broken" }→ both"Working.Red"and"Working.Green"getbreak: "Broken" - Initial states cascade: transitioning to
"Working"goes to"Working.Red"(the child’s initial)
Event Collision
Section titled “Event Collision”When parent and child define the same event, child wins (first-seen policy):
const def = defineMachine( { Parent: defineSubmachine( { Child: undefined }, { Child: { event: "Child" } }, // Child handles "event" "Child" ) }, { Parent: { event: "Parent" } }, // Parent also defines "event" "Parent");
const flat = flattenMachineDefinition(def);// "Parent.Child" has { event: "Parent.Child" } - child transition winsAPI Reference
Section titled “API Reference”defineMachine(states, transitions, initial)
Section titled “defineMachine(states, transitions, initial)”Creates a machine definition object (not a running machine).
defineSubmachine(states, transitions, initial)
Section titled “defineSubmachine(states, transitions, initial)”Marks a state as containing a nested machine. Returns { machine: MachineDefinition }.
flattenMachineDefinition(def, options?)
Section titled “flattenMachineDefinition(def, options?)”Flattens nested definitions into single-level structure.
options.delimiter- Key separator (default:".")
createMachineFromFlat(flatDef)
Section titled “createMachineFromFlat(flatDef)”Creates a running machine from a flattened definition.
createMachineFrom(def)
Section titled “createMachineFrom(def)”Creates a running machine from a non-flattened definition (no nesting support).
Interactive Example
Section titled “Interactive Example”See the Flattened Traffic Light example for a working demo with visualizers.
- Complex nested types may require explicit annotations
- Flattening happens at definition time, not runtime
- The flattened machine is a normal
FactoryMachinewith string keys