假设我有这个(可以说是误导的)代码片段:
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变换器内部工作了
.
我在这做错了什么?我应该以不同方式构建代码吗?
你可以将parseUser
类型概括为
parseUser :: (MonadError String m) => String -> m User
然后它可以在at m ~ Either String
和at m ~ ExceptT String m'
(如果只是Monad m'
)工作,无需任何手动提升.
做到这一点的方法是基本取代Right
与return
和Left
与throwError
在parseUser
的定义.