提升自动提取到ExceptT

rom*_*ovs 7 monads haskell 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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

什么是提升的最标准的方式EitherExceptT?我觉得必须有一些方法,因为Either String是一个实例MonadError.

我写了自己的提升功能:

liftEither :: (Monad m, MonadError a (Either a)) => Either a b -> ExceptT a m b
liftEither = either throwError return
Run Code Online (Sandbox Code Playgroud)

但对我来说,这仍然是错误的,因为我已经在ExceptTmonad变换器内部工作了 .

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

Cac*_*tus 8

你可以将parseUser类型概括为

parseUser :: (MonadError String m) => String -> m User 
Run Code Online (Sandbox Code Playgroud)

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

做到这一点的方法是基本取代RightreturnLeftthrowErrorparseUser的定义.

  • 请注意,该限制`MonadError字符串M` GHC需要`FlexibleContexts`扩展(见http://stackoverflow.com/a/22795830/905686). (2认同)