简洁版本
与此处相同的问题,但在通用MonadResource
实例中而不是在明确的ResourceT m
.
长版
您将如何定义catch
这样的函数:
import Control.Exception (Exception, IOException)
import Control.Monad.Trans.Resource (MonadResource, runResourceT)
catch :: (MonadResource m, Exception e) -> m () -> (e -> m ()) -> m ()
catch = undefined
-- 'a' and 'b' are functions from an external library,
-- so I can't actually change their implementation
a, b :: MonadResource m => m ()
a = -- Something that might throw IO exceptions
b = -- Something that might throw IO exceptions
main :: IO ()
main = runResourceT $ do
a `catch` \(e :: IOException) -> -- Exception handling
b `catch` \(e :: IOException) -> -- Exception handling
我遇到的问题是:
- 在
Control.Exception
,catch
仅适用于裸IO
s ; - 在
Control.Exception.Lifted
,catch
需要一个 的实例MonadBaseControl
,MonadResource
不幸的是不是(我想知道为什么); MonadResource
暗示MonadThrow
它定义了一个monadThrow
没有“catch”等价物的函数(我想知道为什么);
看起来处理IO
异常的唯一方法是退出ResourceT
层,这让我很困扰:我希望能够在本地处理异常,而无需通过 monad 转换器堆栈。
有关信息,在我的真实代码中,a
实际上b
是http
来自Network.HTTP.Conduit
.
感谢您的见解。
有问题的最少代码
ghc --make example.hs
与安装的库一起编译http-conduit
:
{-# LANGUAGE FlexibleContexts, ScopedTypeVariables #-}
import Control.Exception.Lifted (IOException, catch)
import Control.Monad.Base (liftBase)
import Control.Monad.Error (MonadError(..), runErrorT)
import Control.Monad.Trans.Control (MonadBaseControl)
import Control.Monad.Trans.Resource (MonadResource, runResourceT)
import Data.Conduit
import Data.Conduit.List (consume)
import Data.Conduit.Text (decode, utf8)
import Data.Text (Text)
import Network.HTTP.Client
import Network.HTTP.Conduit (http)
main :: IO ()
main = do
result <- runErrorT $ runResourceT f
putStrLn $ "OK: " ++ show result
f :: (MonadBaseControl IO m, MonadResource m, MonadError String m) => m [Text]
f = do
req <- liftBase $ parseUrl "http://uri-that-does-not-exist.abc"
manager <- liftBase $ newManager defaultManagerSettings
response <- (http req manager `catch` \(e :: IOException) -> throwError $ show e)
response $$+- decode utf8 =$ consume
执行时,该程序以错误结束,输出如下:
InternalIOException getAddrInfo: does not exist (Name or service not known)