Skip to content

m-bock/purescript-transit

Repository files navigation

Transit logo

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


CI Pursuit Tutorial Benchmarks

purescript-transit

Table of Contents

Features

  • 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

Documentation

  • 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.

Installation

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

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 :: GraphvizGraph
    graph = TransitGraphviz.generate countDownTransit identity

  FS.writeTextFile UTF8
    "renders/count-down_graph.dot"
    (Graphviz.toDotStr graph)

Contributing

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.

Buy Me a Coffee at ko-fi.com

About

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

Resources

Stars

Watchers

Forks

Contributors 2

  •  
  •