我想在不释放资源的情况下在 runResourceT 中捕获异常,但函数 catch 在 IO 中运行计算。有没有办法在 runResourceT 中捕获异常,或者重构代码的推荐方法是什么?
感谢您的帮助。
{-# LANGUAGE FlexibleContexts #-}
module Main where
import Control.Exception as EX
import Control.Monad.IO.Class
import Control.Monad.Trans.Resource
type Resource = String
allocResource :: IO Resource
allocResource = let r = "Resource"
in putStrLn (r ++ " opened.") >> return r
closeResource :: Resource -> IO ()
closeResource r = putStrLn $ r ++ " closed."
withResource :: ( MonadIO m
, MonadBaseControl IO m
, MonadThrow m
, MonadUnsafeIO m
) => (Resource -> ResourceT m a) -> m a
withResource f = runResourceT $ do
(_, r) <- allocate allocResource closeResource
f r
useResource :: ( MonadIO m
, MonadBaseControl IO m
, MonadThrow m
, MonadUnsafeIO m
) => Resource -> ResourceT m Int
useResource r = liftIO $ putStrLn ("Using " ++ r) >> return 1
main :: IO ()
main = do
putStrLn "Start..."
withResource $ \r -> do
x <- useResource r
{-- This does not compile as the catch computation runs inside IO
y <- liftIO $ EX.catch (useResource r)
(\e -> do putStrLn $ show (e::SomeException)
return 0)
--}
return ()
putStrLn "Done."
答案 0 :(得分:8)
ResourceT
是MonadBaseControl
包中monad-control的一个实例,用于将forkIO
和catch
等控制结构提升为转换后的monad。< / p>
lifted-base包构建在monad-control之上,包含具有适用于任何MonadBaseControl
的标准控制结构版本的模块。对于异常处理,您可以使用Control.Exception.Lifted
模块中的函数。因此,只需import qualified Control.Exception.Lifted as EX
1 ,您的代码应该可以正常工作。
1 注意qualified
这里;非常令人困惑的是,import A as B
实际上将A
中的所有定义导入范围,并简单地将B
定义为模块的别名!您需要使用qualified
来确保定义不会被纳入范围,而是通过B
别名专门访问。
答案 1 :(得分:0)
作为替代方法,您可以使用MonadCatch
包中的ResourceT
exceptions实例。您只需要替换catch
中的Control.Monad.Catch
的通用版本:
import Control.Monad.Catch
…
main = do
…
withResource $ \r -> do
…
y <- Control.Monad.Catch.catch (useResource r) (\e -> …)