Classic Approach
Before diving into Transit, let’s first look at how state machines are typically implemented in PureScript using State and Message data types and an update function performing pattern matching on both. This classic approach is familiar to most PureScript developers and serves as a baseline for understanding what Transit improves upon.
States and Message types
To represent our door in code, we need two major types: the states the door can be in, and the actions that can change those states. In PureScript, we define these as simple data types.
data State
= DoorOpen
| DoorClosed
data Msg
= Close
| Open🗎 test/Examples/Classic/Door.purs L14-L20
The State type captures the two possible states we saw
in the diagram: DoorOpen and DoorClosed. The
Msg type represents the two actions: Close and
Open. These correspond directly to what we visualized
earlier — each state and each transition from the diagram has a
corresponding value in these types.
The update function
Now that we have our types, we need a function that takes the current state and a message, and returns the new state. The traditional way to implement this is with a pattern-matching function:
update :: State -> Msg -> State
update state msg =
case state, msg of
DoorOpen, Close -> DoorClosed
DoorClosed, Open -> DoorOpen
_, _ -> state🗎 test/Examples/Classic/Door.purs L22-L27
We pattern match on both the current state and the message at once.
It could also be written as a nested pattern match. The
update function handles the two valid transitions we saw in
the diagram: closing an open door and opening a closed door. The
catch-all case _, _ -> state handles any invalid
combinations (like trying to open an already open door) by returning the
current state unchanged.
While this approach works and is straightforward, it has some drawbacks:
Implicit state machine specification: The state machine’s structure is only defined implicitly within the update function’s pattern matching and return values.
Documentation drift: If you maintain a state diagram for documentation purposes, there’s nothing ensuring the code stays in sync — you have to remember to update both manually.
Limited analysis capabilities: There’s no way to analyze the state machine’s structure or behavior statically — you can only inspect its behavior by running the code.