Let's say I have this (arguably mislead) piece of code laying around:
import System.Environment (getArgs)
import Control.Monad.Except
parseArgs :: ExceptT String IO User
parseArgs =
do
args <- lift getArgs
case safeHead args of
Just admin -> parseUser admin
Nothing -> throwError "No admin specified"
parseUser :: String -> Either String User
-- implementation elided
safeHead :: [a] -> Maybe a
-- implementation elided
main =
do
r <- runExceptT parseArgs
case r of
Left err -> putStrLn $ "ERROR: " ++ err
Right res -> print res
ghc gives me the following error:
Couldn't match expected type ‘ExceptT String IO User’
with actual type ‘Either String User’
In the expression: parseUser admin
In a case alternative: Just admin -> parseUser admin
What's the most standard way of lifting an Either into an ExceptT?
I feel there must be some way since Either String is an instance of MonadError.
I wrote my own lifting function:
liftEither :: (Monad m, MonadError a (Either a)) => Either a b -> ExceptT a m b
liftEither = either throwError return
But to me this still feels wrong since I'm already working inside the
ExceptT monad transformer.
What am I doing wrong here? Should I structure my code differently?
You can generalise parseUser's type to
parseUser :: (MonadError String m) => String -> m User
and then it would work both at m ~ Either String and at m ~ ExceptT String m' (if only Monad m') without any manual lifting necessary.
The way to do it is to basically replace Right with return and Left with throwError in parseUser's definition.
If you are using transformers instead of mtl, then you can use tryRight from Control.Error.Safe.
tryRight :: Monad m => Either e a -> ExceptT e m a
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With