fho*_*fho 8 error-handling haskell monad-transformers either
我花了一半时间试图弄清楚如何使用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))
Run Code Online (Sandbox Code Playgroud)
只要我只使用ReaderT和StateT函数,这个效果很好.我的印象是,现在我应该可以这样写:
faultyFunction :: String -> Stuff String
faultyFunction s = do
when s == "left" $ left "breaking out"
"right"
Run Code Online (Sandbox Code Playgroud)
更重要的是捕获Either应该可以hoistEither从errors包中获得的返回值:
faultyLookup :: Map -> String -> Stuff String
faultyLookup m k = do
hoistEither $ lookup k m
Run Code Online (Sandbox Code Playgroud)
我阅读了关于monad变形金刚的现实世界haskell章节,并在其中摆弄lift.但我无法得到任何东西.
你不能只是使用的原因left和hoistEither功能是直接,不像StateT和ReaderT来自mtl包,either包不提供类似类型类MonadReader或MonadState.
前面提到的类型类可以透明地处理monad堆栈中的提升,但是EitherT,你必须自己完成提升(或编写MonadEither类似于MonadReaderet al 的类型类).
faultyFunction :: String -> Stuff String
faultyFunction s = do
when (s == "left") $ Stuff $ lift $ lift $ left "breaking out"
return "right"
Run Code Online (Sandbox Code Playgroud)
首先,你需要应用Stuff的包装,然后lift通过ReaderT变压器,然后lift通过再次StateT变压器.
您可能想为自己编写实用程序函数,例如
stuffLeft :: T.Text -> Stuff a
stuffLeft = Stuff . lift . lift . left
Run Code Online (Sandbox Code Playgroud)
然后你可以像这样简单地使用它:
faultyFunction :: String -> Stuff String
faultyFunction s = do
when (s == "left") $ stuffLeft "breaking out"
return "right"
Run Code Online (Sandbox Code Playgroud)
或者,如果为其定义实例,则可以使用Control.Monad.Errorfrom .mtlErrorText
instance Error T.Text where
strMsg = T.pack
Run Code Online (Sandbox Code Playgroud)
现在你可以改变的定义,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
Run Code Online (Sandbox Code Playgroud)
使用此原始faultyFunction类型检查,无需任何手动提升.
您还可以为(使用from )的任何实例编写通用实现left并使用hoistEither它们:MonadErroreitherData.Either
left :: MonadError e m => e -> m a
left = throwError
hoistEither :: MonadError e m => Either e a -> m a
hoistEither = either throwError return
Run Code Online (Sandbox Code Playgroud)