The following problem is full demonstrated in this Gist in a file called Main.purs
. You can try it out by using at Monad Transformer Problem. You'll have to open the Console log in the browser to see the results.
Using catchError
in a Monad Stack where ExceptT
is in the middle of the Stack, will cause everything above ExceptT
in that Stack to be lost and everything below the ExceptT
to be kept.
So imagine the following Stack:
+-------------------+
| WriterT | (is above ExceptT and NOT lost)
+-------------------+
| ExceptT |
+-------------------+
| StateT | (is below ExceptT and is lost)
+-------------------+
Now the following code is the code we want to try to run using catchError
:
validate :: Int -> AppM
validate n = do
s <- get
let x = spy "s in validate" s
log "HEY!!!!!!!!!!!!!!!!!!!" -- This will NOT be lost on the error case
put 10 -- This will be lost on the error case but kept on the success case
when (n == 0) $ void $ throwError "WE CANNOT HAVE A 0 STATE!"
And we're going to call it in the following way:
catchError (validate n) (\err -> do
s <- get
let x = spy "s in error handler" s
log $ "We encountered an error: (" <> err <> ")"
put 100
)
When an error occurs in validate
, the changes to the log made by WriterT
will be kept, but the change to the state made by StateT
will not be.
This behavior is unexpected and subject to change when the Stack Order changes.
While catchError
is an unexpected loss, the loss encountered during a run of a Monad Stack will be apparent in it's return Type.
The return Type for the above Monad Stack is:
Tuple (Either String (Tuple Unit Int)) String
e
is the error Typea
is the Pure Computation Types
is the state Typew
is the log Type
Notice how we lose state when we have an error but not the log. This is the same behavior as catchError's
attempted computation. i.e. validate n
in our above example.