transit

A PureScript library for building fast and type-safe state machines.


Install
psc-package install transit

Documentation

Transit logo

A library for building type-safe state machines.

purescript-transit

Table of Contents

Features

  • State Transitions are specified with 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

Documentation

Installation

(once published to package set:)

spago install transit

Minimal Example: A Count Down State Machine

Full source code: test/Examples/CountDown.purs

Let's consider a simple count down state machine which is described by the following state diagram:

Count Down state diagram

Types

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"
      )

Update Function

Finally, we write the update function that is checked at compile 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 }
  )

Generate State Diagram

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 :: String
    graph = TransitGraphviz.generate_ countDownTransit

  FS.writeTextFile UTF8 "renders/count-down.dot" graph