我花了半天时间试图弄清楚如何使用 EitherT 来处理我的代码中的错误。
我已经定义了一个这样的变压器堆栈。
-- Stuff Monad
data StuffConfig = StuffConfig {
appId :: T.Text,
appSecret :: T.Text
}
data StuffState = StuffState {
stateToken :: Maybe Token,
stateTime :: POSIXTime
}
newtype Stuff a = Stuff {
runStuff :: (ReaderT StuffConfig (StateT StuffState (EitherT T.Text IO))) a
} deriving (Monad, Functor, Applicative,
MonadIO,
MonadReader StuffConfig,
MonadState StuffState
)
askStuff :: StuffConfig -> Stuff a -> IO (Either T.Text a)
askStuff config a = do
t <- getPOSIXTime
runEitherT (evalStateT (runReaderT (runStuff a) config) (StuffState Nothing t))
只要我只使用
ReaderT
就可以很好地工作和 StateT
功能。我的印象是现在我应该能够写出这样的东西:faultyFunction :: String -> Stuff String
faultyFunction s = do
when s == "left" $ left "breaking out"
"right"
更重要的是捕获
Either
hoistEither
应有的返回值来自 errors
包裹:faultyLookup :: Map -> String -> Stuff String
faultyLookup m k = do
hoistEither $ lookup k m
我阅读了关于单子(monad)更改器(mutator)的真实世界haskell章节并摆弄了
lift
.但是我无法进行任何类型检查。
最佳答案
不能只使用 left
的原因和 hoistEither
与 StateT
不同的是,直接功能和 ReaderT
来自 mtl
包, either
包不提供类似于 MonadReader
的类型类或 MonadState
.
上述类型类负责透明地提升 monad 堆栈,但对于 EitherT
,您必须自己完成提升(或编写类似于 MonadEither
等的 MonadReader
类型类)。
faultyFunction :: String -> Stuff String
faultyFunction s = do
when (s == "left") $ Stuff $ lift $ lift $ left "breaking out"
return "right"
首先你需要申请
Stuff
包装器,然后是 lift
超过 ReaderT
变压器,然后 lift
再次通过StateT
变压器。您可能想为自己编写实用程序函数,例如
stuffLeft :: T.Text -> Stuff a
stuffLeft = Stuff . lift . lift . left
然后你可以像这样简单地使用它:
faultyFunction :: String -> Stuff String
faultyFunction s = do
when (s == "left") $ stuffLeft "breaking out"
return "right"
或者,您可以使用
Control.Monad.Error
来自 mtl
, 如果您定义 Error
Text
的实例.instance Error T.Text where
strMsg = T.pack
现在您可以更改
Stuff
的定义实现left
和 hoistEither
像这样:newtype Stuff a = Stuff {
runStuff :: (ReaderT StuffConfig (StateT StuffState (ErrorT T.Text IO))) a
} deriving (Monad, Functor, Applicative,
MonadIO,
MonadReader StuffConfig,
MonadState StuffState,
MonadError T.Text
)
left :: T.Text -> Stuff a
left = throwError
hoistEither :: Either T.Text a -> Stuff a
hoistEither = Stuff . lift . lift . ErrorT . return
有了这个你原来的
faultyFunction
无需任何手动提升即可进行型式检查。您还可以为
left
编写通用实现。和 hoistEither
适用于 MonadError
的任何实例(使用 either
来自 Data.Either
):left :: MonadError e m => e -> m a
left = throwError
hoistEither :: MonadError e m => Either e a -> m a
hoistEither = either throwError return
关于error-handling - EitherT 是如何工作的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14428756/