Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

lift Either to ExceptT automatically

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?

like image 647
romeovs Avatar asked Jan 04 '16 09:01

romeovs


2 Answers

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.

like image 195
Cactus Avatar answered Nov 15 '22 06:11

Cactus


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
like image 33
Xiaokui Shu Avatar answered Nov 15 '22 07:11

Xiaokui Shu