如何在runResourceT中捕获异常

时间:2012-06-08 17:10:15

标签: haskell

我想在不释放资源的情况下在 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."

2 个答案:

答案 0 :(得分:8)

ResourceTMonadBaseControl包中monad-control的一个实例,用于将forkIOcatch等控制结构提升为转换后的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 -> …)