自动将 Either 提升到 ExceptT
lift Either to ExceptT automatically
假设我有这段(可以说是误导的)代码:
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
单子变换器。
我在这里做错了什么?我应该以不同的方式构建我的代码吗?
您可以将 parseUser
的类型概括为
parseUser :: (MonadError String m) => String -> m User
然后它将在 m ~ Either String
和 m ~ ExceptT String m'
(如果只有 Monad m'
)都可以工作,而无需任何手动提升。
实现的方法基本上是在parseUser
的定义中将Right
替换为return
,将Left
替换为throwError
。
如果您使用 transformers
而不是 mtl
,那么您可以使用 Control.Error.Safe
中的 tryRight
。
tryRight :: Monad m => Either e a -> ExceptT e m a
您可以在 transformers
(Control.Monad.Trans.Except) 中使用 except
:
except :: Monad m => Either e a -> ExceptT e m a
定义为评论中建议的ibotty
它与您的 liftEither
有点不同,因为它 MonadError
。您可以在适用 ExceptT 的任何地方使用它。
顺便说一句,已经有一个liftEither
,不同的是:
liftEither :: MonadError e m => Either e a -> m a
.
我觉得这个对你没用。
假设我有这段(可以说是误导的)代码:
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
单子变换器。
我在这里做错了什么?我应该以不同的方式构建我的代码吗?
您可以将 parseUser
的类型概括为
parseUser :: (MonadError String m) => String -> m User
然后它将在 m ~ Either String
和 m ~ ExceptT String m'
(如果只有 Monad m'
)都可以工作,而无需任何手动提升。
实现的方法基本上是在parseUser
的定义中将Right
替换为return
,将Left
替换为throwError
。
如果您使用 transformers
而不是 mtl
,那么您可以使用 Control.Error.Safe
中的 tryRight
。
tryRight :: Monad m => Either e a -> ExceptT e m a
您可以在 transformers
(Control.Monad.Trans.Except) 中使用 except
:
except :: Monad m => Either e a -> ExceptT e m a
定义为评论中建议的ibotty
它与您的 liftEither
有点不同,因为它 MonadError
。您可以在适用 ExceptT 的任何地方使用它。
顺便说一句,已经有一个liftEither
,不同的是:
liftEither :: MonadError e m => Either e a -> m a
.
我觉得这个对你没用。