You're viewing the readable version of this site. The interactive extras (search, diagrams, read-aloud) need JavaScript and a current browser. Enable JavaScript; if it is already enabled, update your browser.

Learn · Hidden Machines · Part I

budding

Independent Lifecycles Need Independent State

State machines and product automata.

state-machines, automata, product-types, software-design, learn

You will recover a finite-state transition from scattered callbacks, separate effects from the pure machine, and keep independent lifecycles independent by composing their states as a product.

A snapshot is not yet a story

The previous chapter gave a request four admissible alternatives. Now two callbacks race: received publishes data and cancel returns to idle. Is received legal after cancellation? The state type alone cannot answer.

Write a row for each pair of current state and event. A row either produces a next state and effect requests or rejects the event. That table is an automaton. Its carrier is the finite state set; its operation is transition.

Prediction — decide whether the event belongs to the machine.

The machine core should not fetch, render, or start timers. It returns effect descriptions. A handler interprets those descriptions at the edge. Tests can then replay start, cancel, received without a network or clock.

One giant enum multiplies unrelated concerns

Add a disclosure panel with closed and open. A tempting enum lists all four pairings: loadingClosed, loadingOpen, loadedClosed, loadedOpen. The request and disclosure lifecycles are independent, so pair them instead:

S=Srequest×Sdisclosure. S = S_{request}\times S_{disclosure}.

Read the multiplication sign as “paired with.” If the request has four states and disclosure has two, the product has at most eight pairs. Each event changes only the coordinate it owns. An invariant may make some pairs unreachable, but an unrelated concern does not infect every transition with new names.

This is a product automaton. It is not a license to centralize the pair in one framework. The two small transition functions can remain separate and a thin composition layer can route events.

The laws are operational

For each event, transition is deterministic: the same state and event produce the same next state and requested effects. Every returned state belongs to the carrier—closure. Invalid events are represented deliberately rather than falling through a callback. Effects occur only when the handler interprets a returned request.

Characterization tests should first pin the observations of the tangled version: published view, requested effects, and their order. Only then can a reference transition table establish that the refactor preserves those observations.

Where the model stops

Do not build a workflow platform for a two-state toggle. A machine earns its name when transition legality matters. Product composition is also wrong when the coordinates are not independent—if opening the door mechanically stops a washer, that cross-invariant belongs in composition and may remove pairs from the reachable set.

Lessons

  • Alternatives plus events form a state machine.
  • A pure transition returns state and effect requests; a handler performs effects.
  • Independent lifecycles compose as a product instead of a god enum.
  • Reachability, not the Cartesian product alone, determines real system states.

Practice

  1. Reconstruct the transition rows for idle --start--> loading --receive--> loaded and cancellation.
  2. Decide whether “online/offline” and “authenticated/anonymous” are independent in a system where logout also closes the connection.
  3. Transfer the model to a washer's door and cycle states.

The machine is cleaner, but how will the next refactor prove it kept the right behavior?

References

  1. David Harel, “Statecharts: A Visual Formalism for Complex Systems”.” — hierarchical and orthogonal state structure.
  2. TypeScript Handbook, “Exhaustiveness checking”.” — making missing alternatives visible to the compiler.