2012-06-08 9 views
8

me gustaría capturar una excepción dentro runResourceT sin liberar el recurso, pero la captura función corre el cálculo interno en IO. ¿Hay alguna manera de detectar una excepción dentro de runResourceT, o cuál es la forma recomendada de refactorizar el código?Cómo coger una excepción dentro runResourceT

Gracias por su ayuda.

{-# 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." 

Respuesta

8

ResourceT es una instancia de MonadBaseControl desde el paquete de monad-control, que está diseñado para la elevación de estructuras de control como forkIO y catch en mónadas transformadas.

El paquete lifted-base, que está construido sobre la monad-control, contiene módulos con versiones de estructuras de control estándar que funcionan en cualquier MonadBaseControl. Para el manejo de excepciones, puede usar las funciones en el módulo Control.Exception.Lifted. Entonces, solo import qualified Control.Exception.Lifted as EX , y su código debería funcionar bien.

Tenga en cuenta el qualified aquí; bastante confuso, import A as B en realidad importa todas las definiciones en A en el alcance, y simplemente define B como un alias para el módulo! Debe utilizar qualified para asegurarse de que las definiciones no se incluyan en el alcance y, en su lugar, se acceda exclusivamente a través del alias B.

+0

Gracias por su respuesta y comentarios. Funciona como se esperaba y me ayuda a comprender mejor los transformadores de mónadas. – jedf

0

Como un enfoque alternativo, puede usar la instancia MonadCatch de ResourceT, que se encuentra en el paquete exceptions. Usted sólo tendrá que sustituir la versión generalizada de catch de Control.Monad.Catch:

import Control.Monad.Catch 
… 
main = do 
    … 
    withResource $ \r -> do 
    … 
    y <- Control.Monad.Catch.catch (useResource r) (\e -> …) 
Cuestiones relacionadas