如何在runResourceT中捕获异常

jed*_*edf 8 haskell

我想在不释放资源的情况下在runResourceT内捕获异常,但函数catchIO内运行计算.有没有办法在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."
Run Code Online (Sandbox Code Playgroud)

ehi*_*ird 8

ResourceT是的一个实例MonadBaseControl单子控制包,其被设计用于提升控制结构等forkIOcatch成经变换的单子.

基于monad-control构建的提升基础软件包包含具有适用于任何版本的标准控制结构版本的模块MonadBaseControl.对于异常处理,您可以使用Control.Exception.Lifted模块中的功能.所以,只需import qualified Control.Exception.Lifted as EX1,而你的代码应该可以正常工作.

1注意qualified这里; 非常容易混淆,import A as B实际上将所有定义A导入范围,并简单地定义B为模块的别名!您需要使用qualified以确保定义不会进入范围,而是通过B别名专门访问.