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:
- Logging state transitions
- Accumulating data during updates
- Performing other side effects
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:
- We use
mkUpdateMinstead ofmkUpdate - We use
matchMinstead ofmatch - The update function returns
m Stateinstead of justState - Inside each match handler, we can perform monadic operations (like
tellfor the Writer monad) - We wrap the return value with
pure $ return @"..."to lift it into the monad
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"
]