Monadic Updates

In some cases, you may want your update function to perform effects or collect information during state transitions. Transit supports monadic update functions through mkUpdateM and matchM, which allow you to work within any monad context.

This is useful for scenarios like:

Let’s see how to create an update function that logs each transition using the Writer monad. This is just an example, you can use any monad.

type Accum = Array String

update :: forall m. MonadWriter Accum m => State -> Msg -> m State
update = mkUpdateM @DoorTransit
  ( matchM @"DoorOpen" @"Close" \_ _ -> do
      tell [ "You just closed the door" ]
      pure $ return @"DoorClosed"
  )
  ( matchM @"DoorClosed" @"Open" \_ _ -> do
      tell [ "You just opened the door" ]
      pure $ return @"DoorOpen"
  )

πŸ—Ž test/Examples/Monadic.purs L13-L24

The key differences from the non-monadic version are:

Now we can use this monadic update function to process a sequence of messages while collecting logs:

walk :: Writer Accum State
walk = do
  let s0 = v @"DoorOpen"
  s1 <- update s0 (v @"Close")
  s2 <- update s1 (v @"Open")
  s3 <- update s2 (v @"Close")
  pure s3

πŸ—Ž test/Examples/Monadic.purs L26-L32

Here we chain multiple state updates in a do-notation, just like any other monadic computation. Each call to update not only returns the next state, but also accumulates logs in the Writer monad.

Let’s verify that the logs are collected correctly:

specLogs :: Spec Unit
specLogs = do
  it "should return the correct state" do
    let
      logs :: Accum
      logs = execWriter walk

    logs `shouldEqual`
      [ "You just closed the door"
      , "You just opened the door"
      , "You just closed the door"
      ]

πŸ—Ž test/Examples/Monadic.purs L34-L45

↑ Back to top