A PureScript library for building fast and type-safe state machines.
Table of Contents
- State transitions are specified with a type-level DSL.
- Compile-time guarantees that state update functions are complete and valid.
- Automatic state diagram generation
- State machine graph analysis
- Optimized for speed
- API Reference on Pursuit.
- The Tutorial provides a guided introduction to the library, including motivation, context, and comparison to classic approaches. Also available as PDF and EPUB.
- The examples folder demonstrates various use cases.
- Benchmark results show performance characteristics compared to classic approaches.
spago install transitFull source code: test/Examples/CountDown.purs
Let's consider a simple count down state machine which is described by the following state diagram:
To implement this state machine with Transit, first we need to define the state and message types as Variants:
type State = Variant
( "Idle" :: {}
, "Counting" :: { count :: Int }
, "Done" :: {}
)
type Msg = Variant
( "Start" :: { initialCount :: Int }
, "Tick" :: {}
, "Reset" :: {}
)Then we need to define the state machine transitions as follows. Note that the last transition has 2 possible return states.
type CountDownTransit =
Transit
:* ("Idle" :@ "Start" >| "Counting")
:* ("Done" :@ "Reset" >| "Idle")
:*
( "Counting" :@ "Tick"
>| "Counting"
>| "Done"
)Finally, we write the update function that is checked at compile time against the state machine specification:
update :: State -> Msg -> State
update = mkUpdate @CountDownTransit
( match @"Idle" @"Start" \_ msg ->
return @"Counting" { count: msg.initialCount }
)
( match @"Done" @"Reset" \_ _ ->
return @"Idle"
)
( match @"Counting" @"Tick" \state _ ->
let
nextCount = state.count - 1
in
if nextCount == 0 then
return @"Done"
else
return @"Counting" { count: nextCount }
)Reflect type-level state machine specification to a term-level representation:
countDownTransit :: TransitCore
countDownTransit = reflectType (Proxy @CountDownTransit)Generate state diagram or perform other analysis on the state machine's runtime representation:
main :: Effect Unit
main = do
let
graph :: GraphvizGraph
graph = TransitGraphviz.generate countDownTransit identity
FS.writeTextFile UTF8
"renders/count-down_graph.dot"
(Graphviz.toDotStr graph)Contributions are welcome! Please open an issue to report bugs, suggest improvements, or propose new examples to be added.
If this project was useful to you, a virtual coffee is appreciated.