Combining Monads and Error Handling

For more advanced scenarios, you can combine error handling with monadic effects using mkUpdateMaybeM.

Here’s an update function that logs transitions AND returns Maybe. Again we use the Writer as example, but you can use any monad you want.

type Accum = Array String

update :: forall m. MonadWriter Accum m => State -> Msg -> m (Maybe State)
update = mkUpdateMaybeM @DoorTransit
  ( matchM @"DoorOpen" @"Close" \_ _ -> do
      tell [ "Closing door" ]
      pure $ return @"DoorClosed"
  )
  ( matchM @"DoorClosed" @"Open" \_ _ -> do
      tell [ "Opening door" ]
      pure $ return @"DoorOpen"
  )

🗎 test/Examples/ErrorHandlingMonadic.purs L15-L26

The return type is m (Maybe State) - combining the monad m (e.g. for effects) with Maybe (for error handling). Inside each handler, we can perform monadic operations like tell to log transitions.

Now let’s use this in a scenario where we attempt an invalid transition in the middle of a sequence:

walk :: MaybeT (Writer Accum) State
walk = do
  let s0 = v @"DoorOpen"
  s1 <- MaybeT $ update s0 (v @"Close")
  s2 <- MaybeT $ update s1 (v @"Open")
  s3 <- MaybeT $ update s2 (v @"Close")
  s4 <- MaybeT $ update s3 (v @"Close") -- here we request illegal transition
  s5 <- MaybeT $ update s4 (v @"Open")
  pure s5

🗎 test/Examples/ErrorHandlingMonadic.purs L28-L36

Here we use the MaybeT transformer to work with the Writer monad while handling potential failures. Notice that somewhere along the way, we attempt an invalid transition (closing an already closed door). This will cause the computation to short-circuit - the subsequent transitions won’t be executed.

Let’s verify that the logs only contain the transitions that actually occurred:

spec :: Spec Unit
spec = do
  describe "ErrorHandlingMonadic" do
    it "should return the correct state" do
      let
        logs :: Array String
        logs = execWriter (runMaybeT walk)
      logs `shouldEqual`
        [ "Closing door"
        , "Opening door"
        , "Closing door"
        ]

🗎 test/Examples/ErrorHandlingMonadic.purs L38-L49

Indeed, the logs stop after the invalid transition.

↑ Back to top