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?
ExceptT . return
?ExceptT = ExceptT (m (Either e a))
, soreturn
gets you toIO (Either String User)
andExceptT
(as constructor/function) toExceptT String IO User
. – LubetliftEither
sounds like the right answer to me (or Cactus's answer about generalizing the type ofparseUser
. – Roughdry