8

我想在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."
4

2 回答 2

8

ResourceTMonadBaseControl来自monad-control包的一个实例,它被设计用于提升控制结构forkIO,例如catch转换为 monad。

建立在 monad-control 之上的提升基础MonadBaseControl包包含具有标准控制结构版本的模块,可在任何. 对于异常处理,您可以使用Control.Exception.Lifted模块中的函数。因此,只需import qualified Control.Exception.Lifted as EX1即可,您的代码应该可以正常工作。

1注意qualified这里;相当令人困惑的是,import A as B实际上将所有定义A导入到范围内,并简单地定义B为模块的别名!您需要使用qualified以确保定义不会被纳入范围,而是通过B别名以独占方式访问。

于 2012-06-08T19:06:09.307 回答
0

作为替代方法,您可以使用异常包中的 ,MonadCatch实例。您只需替换from的通用版本:ResourceTcatchControl.Monad.Catch

import Control.Monad.Catch
…
main = do
  …
  withResource $ \r -> do
    …
    y <- Control.Monad.Catch.catch (useResource r) (\e -> …)
于 2015-03-18T22:30:59.040 回答