module Control.Effect.Bracket ( -- * Effects Bracket(..) , ExitCase(..) -- * Actions , generalBracket , bracket , bracket_ , bracketOnError , onError , finally -- * Interpretations , bracketToIO , bracketToIOUnsafe , runBracketLocally , ignoreBracket -- * Threading utilities , threadBracketViaClass -- * MonadMask , C.MonadMask -- * Carriers , BracketToIOC , BracketToIOUnsafeC , BracketLocallyC , IgnoreBracketC ) where import Control.Effect import Control.Effect.Primitive import Control.Effect.Type.Bracket import Control.Monad import Control.Monad.Catch (MonadMask) import qualified Control.Monad.Catch as C generalBracket :: Eff Bracket m => m a -> (a -> ExitCase b -> m c) -> (a -> m b) -> m (b, c) generalBracket acquire release use = send (GeneralBracket acquire release use) {-# INLINE generalBracket #-} bracket :: Eff Bracket m => m a -> (a -> m c) -> (a -> m b) -> m b bracket acquire release use = do (b, _) <- generalBracket acquire (\a _ -> release a) use return b {-# INLINE bracket #-} bracket_ :: Eff Bracket m => m a -> m c -> m b -> m b bracket_ acquire release use = bracket acquire (const release) (const use) {-# INLINE bracket_ #-} bracketOnError :: Eff Bracket m => m a -> (a -> m c) -> (a -> m b) -> m b bracketOnError acquire release use = do (b, _) <- generalBracket acquire (\a -> \case ExitCaseSuccess _ -> pure () _ -> void $ release a ) use return b {-# INLINE bracketOnError #-} onError :: Eff Bracket m => m a -> m b -> m a onError m h = bracketOnError (pure ()) (const h) (const m) {-# INLINE onError #-} finally :: Eff Bracket m => m a -> m b -> m a finally m h = bracket (pure ()) (const h) (const m) {-# INLINE finally #-} data BracketToIOH instance (Carrier m, MonadMask m) => PrimHandler BracketToIOH Bracket m where effPrimHandler (GeneralBracket acquire release use) = C.generalBracket acquire (\a ec -> C.uninterruptibleMask_ (release a ec)) use {-# INLINEABLE effPrimHandler #-} type BracketToIOC = InterpretPrimC BracketToIOH Bracket -- | Run a 'Bracket' effect that protects against -- any abortive computation of any effect, as well -- as any IO exceptions and asynchronous exceptions. -- -- Any 'generalBracket' will have its cleanup action be executed -- /uninterruptibly masked/. -- This means that the cleanup action /cannot/, under any circumstance, -- be interrupted by asynchronous exceptions. -- -- @'Derivs' ('BracketToIOC' m) = 'Bracket' ': 'Derivs' m@ -- -- @'Prims' ('BracketToIOC' m) = 'Bracket' ': 'Prims' m@ bracketToIO :: (Carrier m, MonadMask m) => BracketToIOC m a -> m a bracketToIO = interpretPrimViaHandler {-# INLINE bracketToIO #-} data BracketToIOUnsafeH instance (Carrier m, MonadMask m) => PrimHandler BracketToIOUnsafeH Bracket m where effPrimHandler (GeneralBracket acquire release use) = C.generalBracket acquire release use {-# INLINEABLE effPrimHandler #-} type BracketToIOUnsafeC = InterpretPrimC BracketToIOUnsafeH Bracket -- | Run a 'Bracket' effect that protects against -- any abortive computation of any effect, as well -- as any IO exceptions and asynchronous exceptions. -- -- Any 'generalBracket' will have its cleanup action -- be executed /interruptibly masked/. -- This means that the cleanup action /can/ be interrupted by -- asynchronous exceptions if the cleanup action executes -- interruptible (blocking) operations, -- such as 'Control.Concurrent.MVar.putMVar'. -- -- @'Derivs' ('BracketToIOUnsafeC' m) = 'Bracket' ': 'Derivs' m@ -- -- @'Prims' ('BracketToIOUnsafeC' m) = 'Bracket' ': 'Prims' m@ -- -- @since 0.2.0.0 bracketToIOUnsafe :: (Carrier m, MonadMask m) => BracketToIOUnsafeC m a -> m a bracketToIOUnsafe = interpretPrimViaHandler {-# INLINE bracketToIOUnsafe #-} data BracketLocallyH instance Carrier m => PrimHandler BracketLocallyH Bracket m where effPrimHandler (GeneralBracket acquire release use) = do a <- acquire b <- use a c <- release a (ExitCaseSuccess b) return (b, c) {-# INLINEABLE effPrimHandler #-} type BracketLocallyC = InterpretPrimC BracketLocallyH Bracket -- | Run a 'Bracket' effect that protects against -- any abortive computations of purely local effects -- -- i.e. effects interpreted before 'runBracketLocally' -- that are not interpreted in terms of the final monad -- nor other effects interpreted after 'runBracketLocally'. -- -- This does /not/ protect against IO exceptions of any kind, -- including asynchronous exceptions. -- -- This is more situational compared to 'bracketToIO', -- but can be useful. For an example, see the [wiki](https://github.com/KingoftheHomeless/in-other-words/wiki/Advanced-topics#bracket). -- -- @'Derivs' ('BracketLocallyC' m) = 'Bracket' ': 'Derivs' m@ -- -- @'Prims' ('BracketLocallyC' m) = 'Bracket' ': 'Prims' m@ runBracketLocally :: Carrier m => BracketLocallyC m a -> m a runBracketLocally = interpretPrimViaHandler {-# INLINE runBracketLocally #-} type IgnoreBracketC = InterpretC IgnoreBracketH Bracket data IgnoreBracketH instance Carrier m => Handler IgnoreBracketH Bracket m where effHandler (GeneralBracket acquire release use) = do a <- acquire b <- use a c <- release a (ExitCaseSuccess b) return (b, c) {-# INLINEABLE effHandler #-} -- | Run a 'Bracket' effect by ignoring it, providing no protection at all. -- -- @'Derivs' ('IgnoreBracketC' m) = 'Bracket' ': 'Derivs' m@ -- -- @'Prims' ('IgnoreBracketC' m) = 'Prims' m@ ignoreBracket :: Carrier m => IgnoreBracketC m a -> m a ignoreBracket = interpretViaHandler {-# INLINE ignoreBracket #-}