From d15bdc99b101ad21039bce031cef972763fd9838 Mon Sep 17 00:00:00 2001
From: Michael Snoyman <michael@snoyman.com>
Date: Tue, 6 Mar 2018 19:45:16 +0200
Subject: [PATCH] Add moreGeneralBracket #63

---
 src/Control/Monad/Catch.hs | 85 ++++++++++++++++++++++++++------------
 1 file changed, 59 insertions(+), 26 deletions(-)

diff --git a/src/Control/Monad/Catch.hs b/src/Control/Monad/Catch.hs
index 4930869..cecad53 100644
--- a/src/Control/Monad/Catch.hs
+++ b/src/Control/Monad/Catch.hs
@@ -249,6 +249,24 @@ class MonadCatch m => MonadMask m where
     -> (a -> m b)
     -- ^ inner action to perform with the resource
     -> m b
+  generalBracket acquire release releaseEx use = moreGeneralBracket
+    const
+    acquire
+    use
+    (\a et ->
+       case et of
+         ETException e -> releaseEx a e >> return ()
+         _ -> release a >> return ())
+
+  moreGeneralBracket
+    :: (b -> c -> d)
+    -> m a                       -- ^ acquire
+    -> (a -> m b)                -- ^ use
+    -> (a -> ExitType b -> m c)  -- ^ release
+    -> m d
+
+data ExitType a = ETSuccess a | ETException SomeException | ETOther
+
 
 instance MonadThrow [] where
   throwM _ = []
@@ -264,13 +282,13 @@ instance MonadCatch IO where
 instance MonadMask IO where
   mask = ControlException.mask
   uninterruptibleMask = ControlException.uninterruptibleMask
-  generalBracket acquire release cleanup use = mask $ \unmasked -> do
+  moreGeneralBracket f acquire use release = mask $ \unmasked -> do
     resource <- acquire
     result <- unmasked (use resource) `catch` \e -> do
-      _ <- cleanup resource e
+      _ <- release resource (ETException e)
       throwM e
-    _ <- release resource
-    return result
+    c <- release resource $ ETSuccess result
+    return $ f result c
 
 instance MonadThrow STM where
   throwM = STM.throwSTM
@@ -314,12 +332,12 @@ instance MonadMask m => MonadMask (IdentityT m) where
       where q :: (m a -> m a) -> IdentityT m a -> IdentityT m a
             q u = IdentityT . u . runIdentityT
 
-  generalBracket acquire release cleanup use = IdentityT $
-    generalBracket
+  moreGeneralBracket f acquire use release = IdentityT $
+    moreGeneralBracket
+      f
       (runIdentityT acquire)
-      (runIdentityT . release)
-      (\resource e -> runIdentityT (cleanup resource e))
-      (\resource -> runIdentityT (use resource))
+      (runIdentityT . use)
+      (\resource et -> runIdentityT (release resource et))
 
 instance MonadThrow m => MonadThrow (LazyS.StateT s m) where
   throwM e = lift $ throwM e
@@ -334,18 +352,21 @@ instance MonadMask m => MonadMask (LazyS.StateT s m) where
       where q :: (m (a, s) -> m (a, s)) -> LazyS.StateT s m a -> LazyS.StateT s m a
             q u (LazyS.StateT b) = LazyS.StateT (u . b)
 
-  generalBracket acquire release cleanup use = LazyS.StateT $ \s0 ->
-    generalBracket
+  moreGeneralBracket f acquire use release = LazyS.StateT $ \s0 ->
+    moreGeneralBracket
+      (\(b, _) (c, s) -> (f b c, s))
+
       (LazyS.runStateT acquire s0)
 
-      -- Note that we're reverting to s1 here, the state after the
-      -- acquire step, and _not_ getting the state from the successful
-      -- run of the inner action. This is because we may be on top of
-      -- something like ExceptT, where no updated state is available.
-      (\(resource, s1) -> LazyS.runStateT (release resource) s1)
-      (\(resource, s1) e -> LazyS.runStateT (cleanup resource e) s1)
       (\(resource, s1) -> LazyS.runStateT (use resource) s1)
 
+      (\(resource, s1) et ->
+         case et of
+           ETSuccess (b, s2) -> LazyS.runStateT (release resource (ETSuccess b)) s2
+           ETException e -> LazyS.runStateT (release resource (ETException e)) s1
+           ETOther -> LazyS.runStateT (release resource ETOther) s1
+      )
+
 instance MonadThrow m => MonadThrow (StrictS.StateT s m) where
   throwM e = lift $ throwM e
 instance MonadCatch m => MonadCatch (StrictS.StateT s m) where
@@ -547,18 +568,30 @@ instance MonadMask m => MonadMask (ExceptT e m) where
         -> ExceptT e m a -> ExceptT e m a
       q u (ExceptT b) = ExceptT (u b)
 
-  generalBracket acquire release cleanup use = ExceptT $
-    generalBracket
+  moreGeneralBracket f acquire use release = ExceptT $
+    moreGeneralBracket
+      (\eb ec ->
+         case (eb, ec) of
+           (Right b, Right c) -> Right (f b c)
+           (Left e, _) -> Left e
+           (Right _, Left e) -> Left e
+      )
       (runExceptT acquire)
       (\eresource ->
-        case eresource of
-          Left _ -> return ()
-          Right resource -> runExceptT (release resource) >> return ())
-      (\eresource e ->
          case eresource of
-           Left _ -> return ()
-           Right resource -> runExceptT (cleanup resource e) >> return ())
-      (either (return . Left) (runExceptT . use))
+           Left e -> return (Left e)
+           Right resource -> runExceptT (use resource)
+      )
+      (\eresource et ->
+         case eresource of
+           Left e -> return (Left e)
+           Right resource -> runExceptT $ release resource $
+             case et of
+               ETSuccess (Left _) -> ETOther
+               ETSuccess (Right b) -> ETSuccess b
+               ETException e -> ETException e
+               ETOther -> ETOther
+      )
 
 instance MonadThrow m => MonadThrow (ContT r m) where
   throwM = lift . throwM
-- 
GitLab