Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to catch an exception inside runResourceT

Tags:

haskell

I would like to catch an exception inside runResourceT without releasing the resource, but the function catch runs the computation inside IO. Is there a way to catch an exception inside runResourceT, or what is the recommended way to refactor the code ?

Thank you for your help.

{-# 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."
like image 872
jedf Avatar asked Jun 08 '12 17:06

jedf


1 Answers

ResourceT is an instance of MonadBaseControl from the monad-control package, which is designed for lifting control structures like forkIO and catch into transformed monads.

The lifted-base package, which is built on top of monad-control, contains modules with versions of standard control structures that work in any MonadBaseControl. For exception handling, you can use the functions in the Control.Exception.Lifted module. So, just import qualified Control.Exception.Lifted as EX1 instead, and your code should work fine.

1 Note the qualified here; quite confusingly, import A as B actually imports all of the definitions in A into scope, and simply defines B as an alias for the module! You need to use qualified to ensure that the definitions are not brought into scope, and are instead accessed exclusively through the B alias.

like image 62
ehird Avatar answered Nov 18 '22 13:11

ehird