假设我有这个(可以说是误导的)一段代码:
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 String
是MonadError
的实例。
我写了自己的提升功能:
liftEither :: (Monad m, MonadError a (Either a)) => Either a b -> ExceptT a m b
liftEither = either throwError return
但对我来说,这仍然是错误的,因为我已经在内部工作了
ExceptT
monad变压器。
我在这里做错了什么?我应该以不同的方式构建我的代码吗?
答案 0 :(得分:8)
您可以将parseUser
的类型概括为
parseUser :: (MonadError String m) => String -> m User
然后它可以在m ~ Either String
和m ~ 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
。
我不认为这个对你有用。