提升自动提取到ExceptT

时间:2016-01-04 09:55:07

标签: haskell monads monad-transformers either lifting

假设我有这个(可以说是误导的)一段代码:

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给出了以下错误:

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

Either提升为ExceptT的最标准方法是什么? 我认为必须有一些方法,因为Either StringMonadError的实例。

我写了自己的提升功能:

liftEither :: (Monad m, MonadError a (Either a)) => Either a b -> ExceptT a m b
liftEither = either throwError return

但对我来说,这仍然是错误的,因为我已经在内部工作了 ExceptT monad变压器。

我在这里做错了什么?我应该以不同的方式构建我的代码吗?

3 个答案:

答案 0 :(得分:8)

您可以将parseUser的类型概括为

parseUser :: (MonadError String m) => String -> m User 

然后它可以在m ~ Either Stringm ~ ExceptT String m'(如果只有Monad m')工作,无需任何手动提升。

这样做的方法是在Right的定义中基本上将return替换为Left,将throwError替换为parseUser

答案 1 :(得分:0)

如果您使用transformers代替mtl,则可以使用Control.Error.Safe中的tryRight

tryRight :: Monad m => Either e a -> ExceptT e m a

答案 2 :(得分:0)

您可以在 except (Control.Monad.Trans.Except) 中使用 transformers

except :: Monad m => Either e a -> ExceptT e m a

它被定义为评论中建议的 ibotty。

它与您的 liftEither 有点不同,因为它没有 MonadError。您可以在适用于 ExtT 的任何地方使用它。

顺便说一句,已经有一个liftEither,这是不同的:

liftEither :: MonadError e m => Either e a -> m a

我不认为这个对你有用。