Let's say I have the following functions:
checkA :: a -> Either err b
checkA = undefined
checkB :: b -> ST s (Either err c)
checkB = undefined
check :: a -> ST s (Either err c)
check a = either (return . Left) checkB (checkA a)
Is there any way to write check
such that it doesn't require using return . Left
? Usually I'd do something like >>=
, but in this case the return of checkB
is wrapped inside another state monad, so it doesn't work. The other constraint is that checkB
should only run if checkA a
evaluates to Right
, and should just fail with the error on Left
To generalize, are there any standard approaches to using nested monads?
Here's one way to do it with ExceptT
:
checkA :: a -> Either err b
checkA = undefined
checkB :: b -> ExceptT err (ST s) c
checkB = undefined
check :: a -> ExceptT err (ST s) c
check a = except (checkA a) >>= checkB
-- or
check = except . checkA >=> checkB
except
turns Either err b
into Monad m => ExceptT err m b
, and then you can do everything else in the ExceptT err (ST s)
monad.
As a general rule, ExceptT
is a great way to work with monadic actions that may fail when you usually want to bail on failure. The main exception is when the underlying monad is IO
, in which case it's more common to use the built-in exception features from Control.Exception
.
Of course, if you only need one monadic bind, ExceptT
seems a bit like overkill, but once you need more it definitely makes sense.