bracket (and similar functions) discards the ExceptionContext associated with an exception.
For example, the following code:
{-# LANGUAGE DeriveAnyClass #-}
import Control.Exception as BaseException
import Control.Exception.Annotation
import Control.Exception.Context
import GHC.Generics
import Control.Monad.Trans.Resource
import Control.Monad.Trans
import Control.Exception.Safe as SafeException
data Ann = Ann String
deriving (Generic, ExceptionAnnotation, Show)
data Exc = Exc String
deriving (Generic, Exception, Show)
main = do
BaseException.bracket_ (pure ()) (pure ()) $ do
annotateIO (Ann "I'm a nice annotation") $ BaseException.throwIO (Exc "failure!!")
When run, with ghc-9.12.2 and safe-exception-0.1.7.4 would result into:
$ runhaskell BracketExceptionHandling.hs
BracketExceptionHandling.hs: Uncaught exception ghc-internal:GHC.Internal.Exception.ErrorCall:
Exc "failure!!"
Ann "I'm a nice annotation"
HasCallStack backtrace:
throwIO, called at BracketExceptionHandling.hs:18:52 in main:Main
However, replace BaseException.bracket_ by SafeException.bracket_ and you'll get:
runhaskell BracketExceptionHandling.hs
BracketExceptionHandling.hs: Uncaught exception ghc-internal:GHC.Internal.Exception.ErrorCall:
Exc "failure!!"
While handling Exc "failure!!"
HasCallStack backtrace:
bracket_, called at BracketExceptionHandling.hs:17:7 in main:Main
I was initially surprised by this behavior because my initial (and wrong) understanding is that bracket_ from safe-exceptions behaves the same as the one from base: it does not care about the fact that the exception is async or not, so I initially thought that it was a reexport.
However it behaves differently: it uses uninterruptibleMask.
bracket(and similar functions) discards theExceptionContextassociated with an exception.For example, the following code:
{-# LANGUAGE DeriveAnyClass #-} import Control.Exception as BaseException import Control.Exception.Annotation import Control.Exception.Context import GHC.Generics import Control.Monad.Trans.Resource import Control.Monad.Trans import Control.Exception.Safe as SafeException data Ann = Ann String deriving (Generic, ExceptionAnnotation, Show) data Exc = Exc String deriving (Generic, Exception, Show) main = do BaseException.bracket_ (pure ()) (pure ()) $ do annotateIO (Ann "I'm a nice annotation") $ BaseException.throwIO (Exc "failure!!")When run, with
ghc-9.12.2andsafe-exception-0.1.7.4would result into:However, replace
BaseException.bracket_bySafeException.bracket_and you'll get:I was initially surprised by this behavior because my initial (and wrong) understanding is that
bracket_fromsafe-exceptionsbehaves the same as the one frombase: it does not care about the fact that the exception is async or not, so I initially thought that it was a reexport.However it behaves differently: it uses
uninterruptibleMask.