Hierarchical Machines
What are Hierarchical Machines?
Section titled “What are Hierarchical Machines?”Hierarchical State Machines (HSMs) allow you to build nested state machines by defining child states within parent states. Matchina flattens these definitions into a single-level machine with dot-notation state keys.
Key benefits:
- Modular definition - Define nested structure, run as flat machine
- Simple runtime - No nested machine instances to manage
- Type-safe transitions - Same
send()API with compound states like"Working.Red"
When to Use Hierarchy
Section titled “When to Use Hierarchy”Use hierarchy only when a state temporarily delegates control and must return it when finished. If states merely describe what is true, use a flat machine.
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
Interactive Example
Section titled “Interactive Example”Source Code
Section titled “Source Code”import { createHSM } from "matchina/hsm";
export function createFlatTrafficLight() { return createHSM({ initial: "Working", states: { Broken: { on: { repair: "Working", maintenance: "Maintenance", }, },
// Working is a hierarchical state with light cycle substates Working: { initial: "Red", states: { Red: { on: { tick: "Green", }, }, Green: { on: { tick: "Yellow", }, }, Yellow: { on: { tick: "Red", }, }, }, // Parent-level transitions apply to all child states on: { break: "^Broken", maintenance: "^Maintenance", }, },
Maintenance: { on: { complete: "Working", }, }, }, });}import { defineStates, matchina } from "matchina";import { submachine, nestedHsmRoot } from "matchina/hsm";
// 1. Define the Child Machine (Light Cycle)// We need a factory for the child machine so it can be instantiated freshlyconst lightCycleStates = defineStates({ Red: undefined, Green: undefined, Yellow: undefined,});
const createLightCycle = () => matchina( lightCycleStates, { Red: { tick: "Green" }, Green: { tick: "Yellow" }, Yellow: { tick: "Red" }, }, lightCycleStates.Red() );
// 2. Define the Parent Machine (Controller)// We use `submachine` to embed the child machine factoryconst controllerStates = defineStates({ Broken: undefined, Working: submachine(createLightCycle), Maintenance: undefined,});
const createController = () => matchina( controllerStates, { Broken: { repair: "Working", maintenance: "Maintenance" }, Working: { break: "Broken", maintenance: "Maintenance" }, Maintenance: { complete: "Working" }, }, controllerStates.Working() );
// 3. Create the Hierarchical Machine// This wraps the controller with propagation logicexport function createPropagatingTrafficLight() { const root = createController(); return nestedHsmRoot(root);}Creating Hierarchical Machines
Section titled “Creating Hierarchical Machines”Option 1: Declarative Hierarchy (Recommended)
Section titled “Option 1: Declarative Hierarchy (Recommended)”Use createHSM for clean hierarchical definition. This approach defines the hierarchy once and auto-resolves relative transitions. See the Hierarchical Traffic Light example for a complete working implementation.
const machine = createHSM({ initial: "Working", states: { Working: { initial: "Red", states: { Red: { on: { tick: "Green" } }, Green: { on: { tick: "Yellow" } }, Yellow: { on: { tick: "Red" } }, }, on: { break: "Broken", maintenance: "Maintenance" } }, Broken: { on: { repair: "Working" } }, Maintenance: { on: { complete: "Working" } } }});Key concepts:
- Define hierarchy naturally:
WorkingcontainsRed,Green,Yellow - Parent transitions apply to all child states
- Child transitions are specific to that state
- Library handles flattening automatically
Option 2: Runtime Propagation (Advanced)
Section titled “Option 2: Runtime Propagation (Advanced)”For cases where you need actual nested machine instances, use submachine and nestedHsmRoot. This approach creates separate machine instances that communicate via event propagation.
// Child machine factoryconst createLightCycle = () => matchina( lightStates, { Red: { tick: "Green" }, Green: { tick: "Yellow" }, Yellow: { tick: "Red" } }, "Red");
// Parent machine with embedded childconst controllerStates = defineStates({ Broken: undefined, Working: submachine(createLightCycle), Maintenance: undefined,});
const createController = () => matchina( controllerStates, { Broken: { repair: "Working" }, Working: { break: "Broken" }, Maintenance: { complete: "Working" } }, "Working");
// Enable hierarchical propagationexport function createPropagatingTrafficLight() { const root = createController(); return nestedHsmRoot(root);}How Hierarchy Works
Section titled “How Hierarchy Works”Internal State Keys
Section titled “Internal State Keys”The library internally converts nested states to dot-notation keys:
Workingwith childRed→"Working.Red"Workingwith childGreen→"Working.Green"
Important: You define hierarchy naturally - the library handles the flattening internally. You never write "Working.Red" in your definitions.
Transition Resolution
Section titled “Transition Resolution”- Child transitions are automatically 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 machine = createHSM({ states: { Working: { states: { Red: { on: { tick: "Green" } }, // Child handles "tick" }, on: { tick: "Yellow" } // Parent also defines "tick" } }});// Child wins - "Working.Red" uses its own transitionSee the Hierarchical Traffic Light example for concrete collision handling.
Related APIs
Section titled “Related APIs”Key functions for hierarchical machines:
createHSM()- Clean hierarchical definition with automatic flatteningsubmachine()- Wrap machine factories for nested instancesnestedHsmRoot()- Enable event propagation between nested machines
Related Examples
Section titled “Related Examples”- Hierarchical Traffic Light - Complete HSM implementation
- Hierarchical Checkout - Complex nested payment flow
- Hierarchical Combobox - UI component with nested states