10

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

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将 a 提升为的最标准方法是ExceptT什么?我觉得必须有某种方式,因为Either StringMonadError.

我写了自己的提升函数:

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

但对我来说,这仍然感觉不对,因为我已经在 ExceptT单子变压器内部工作了。

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

4

3 回答 3

10

parseUser您可以将的类型概括为

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

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

做到这一点的方法基本上是RightreturnLeftwith throwErrorinparseUser的定义替换。

于 2016-01-04T12:03:16.940 回答
3

如果您使用transformers而不是mtl,那么您可以使用tryRightfrom Control.Error.Safe

tryRight :: Monad m => Either e a -> ExceptT e m a
于 2018-06-06T21:36:10.047 回答
0

您可以excepttransformers(Control.Monad.Trans.Except)中使用:

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

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

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

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

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

我不认为这个对你有用。

于 2021-08-03T15:23:10.090 回答