Search code examples
haskellmonad-transformersstate-monad

Handling Either and ST monads


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?


Solution

  • 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.