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

ResourceTは、変換されたモナドのような制御構造を持ち上げるために設計されMonadBaseControlモナド制御パッケージのインスタンスです。forkIOcatch

monad-controlの上に構築されたlifted-baseパッケージには、任意ので機能する標準制御構造のバージョンを備えたモジュールが含まれていますMonadBaseControlControl.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 に答える