2012-06-08 12 views
8

Vorrei rilevare un'eccezione all'interno runResourceT senza rilasciare la risorsa, ma la cattura funzione di gestisce il calcolo all'interno IO. C'è un modo per rilevare un'eccezione all'interno di runResourceT o qual è il modo consigliato di ridefinire il codice?Come catturare un'eccezione all'interno runResourceT

Grazie per il vostro aiuto.

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

risposta

8

ResourceT è un'istanza di MonadBaseControl dal pacchetto monad-control, che è progettato per il sollevamento di strutture di controllo come forkIO e catch in monadi trasformate.

Il pacchetto lifted-base, che si basa su monad-control, contiene moduli con versioni di strutture di controllo standard che funzionano in qualsiasi MonadBaseControl. Per la gestione delle eccezioni, è possibile utilizzare le funzioni nel modulo Control.Exception.Lifted. Quindi, solo invece, e il tuo codice dovrebbe funzionare correttamente.

Nota il qualified qui; abbastanza confusamente, import A as B importa effettivamente tutte le definizioni in A nell'ambito e definisce semplicemente B come alias per il modulo! È necessario utilizzare qualified per garantire che le definizioni non vengano portate nell'ambito e che vengano invece accessibili esclusivamente tramite l'alias B.

+0

Grazie per la risposta e commenti. Funziona come previsto e mi aiuta a capire meglio i trasformatori di monadi. – jedf

0

Come approccio alternativo, è possibile utilizzare l'istanza MonadCatch di ResourceT, trovata nel pacchetto exceptions. Hai semplicemente bisogno di sostituire la versione generalizzata di catch da Control.Monad.Catch:

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