bracket package:safe-exceptions

Async safe version of bracket
Async safe version of bracketOnError
A variant of bracketOnError where the return value from the first computation is not required.
Async safe version of bracket with access to the exception in the cleanup action.
Async safe version of bracket_
A generalized version of bracket which uses ExitCase to distinguish the different exit cases, and returns the values of both the use and release actions. In practice, this extra information is rarely needed, so it is often more convenient to use one of the simpler functions which are defined in terms of this one, such as bracket, finally, onError, and bracketOnError. This function exists because in order to thread their effects through the execution of bracket, monad transformers need values to be threaded from use to release and from release to the output value. NOTE This method was added in version 0.9.0 of this library. Previously, implementation of functions like bracket and finally in this module were based on the mask and uninterruptibleMask functions only, disallowing some classes of tranformers from having MonadMask instances (notably multi-exit-point transformers like ExceptT). If you are a library author, you'll now need to provide an implementation for this method. The StateT implementation demonstrates most of the subtleties:
generalBracket acquire release use = StateT $ s0 -> do
((b, _s2), (c, s3)) <- generalBracket
(runStateT acquire s0)
((resource, s1) exitCase -> case exitCase of
ExitCaseSuccess (b, s2) -> runStateT (release resource (ExitCaseSuccess b)) s2

-- In the two other cases, the base monad overrides use's state
-- changes and the state reverts to s1.
ExitCaseException e     -> runStateT (release resource (ExitCaseException e)) s1
ExitCaseAbort           -> runStateT (release resource ExitCaseAbort) s1
)
((resource, s1) -> runStateT (use resource) s1)
return ((b, c), s3)
The StateT s m implementation of generalBracket delegates to the m implementation of generalBracket. The acquire, use, and release arguments given to StateT's implementation produce actions of type StateT s m a, StateT s m b, and StateT s m c. In order to run those actions in the base monad, we need to call runStateT, from which we obtain actions of type m (a, s), m (b, s), and m (c, s). Since each action produces the next state, it is important to feed the state produced by the previous action to the next action. In the ExitCaseSuccess case, the state starts at s0, flows through acquire to become s1, flows through use to become s2, and finally flows through release to become s3. In the other two cases, release does not receive the value s2, so its action cannot see the state changes performed by use. This is fine, because in those two cases, an error was thrown in the base monad, so as per the usual interaction between effects in a monad transformer stack, those state changes get reverted. So we start from s1 instead. Finally, the m implementation of generalBracket returns the pairs (b, s) and (c, s). For monad transformers other than StateT, this will be some other type representing the effects and values performed and returned by the use and release actions. The effect part of the use result, in this case _s2, usually needs to be discarded, since those effects have already been incorporated in the release action. The only effect which is intentionally not incorporated in the release action is the effect of throwing an error. In that case, the error must be re-thrown. One subtlety which is easy to miss is that in the case in which use and release both throw an error, the error from release should take priority. Here is an implementation for ExceptT which demonstrates how to do this.
generalBracket acquire release use = ExceptT $ do
(eb, ec) <- generalBracket
(runExceptT acquire)
(eresource exitCase -> case eresource of
Left e -> return (Left e) -- nothing to release, acquire didn't succeed
Right resource -> case exitCase of
ExitCaseSuccess (Right b) -> runExceptT (release resource (ExitCaseSuccess b))
ExitCaseException e       -> runExceptT (release resource (ExitCaseException e))
_                         -> runExceptT (release resource ExitCaseAbort))
(either (return . Left) (runExceptT . use))
return $ do
-- The order in which we perform those two Either effects determines
-- which error will win if they are both Lefts. We want the error from
-- release to win.
c <- ec
b <- eb
return (b, c)