packages feed

exceptions 0.9.0 → 0.10.0

raw patch · 6 files changed

+479/−185 lines, 6 filesdep ~QuickCheckdep ~template-haskellPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: QuickCheck, template-haskell

API changes (from Hackage documentation)

- Control.Monad.Catch: instance e ~ GHC.Exception.SomeException => Control.Monad.Catch.MonadCatch (Data.Either.Either e)
- Control.Monad.Catch: instance e ~ GHC.Exception.SomeException => Control.Monad.Catch.MonadMask (Data.Either.Either e)
- Control.Monad.Catch: instance e ~ GHC.Exception.SomeException => Control.Monad.Catch.MonadThrow (Data.Either.Either e)
+ Control.Monad.Catch: ExitCaseAbort :: ExitCase a
+ Control.Monad.Catch: ExitCaseException :: SomeException -> ExitCase a
+ Control.Monad.Catch: ExitCaseSuccess :: a -> ExitCase a
+ Control.Monad.Catch: data ExitCase a
+ Control.Monad.Catch: instance (e ~ GHC.Exception.SomeException) => Control.Monad.Catch.MonadCatch (Data.Either.Either e)
+ Control.Monad.Catch: instance (e ~ GHC.Exception.SomeException) => Control.Monad.Catch.MonadMask (Data.Either.Either e)
+ Control.Monad.Catch: instance (e ~ GHC.Exception.SomeException) => Control.Monad.Catch.MonadThrow (Data.Either.Either e)
+ Control.Monad.Catch: instance Control.Monad.Catch.MonadMask m => Control.Monad.Catch.MonadMask (Control.Monad.Trans.Maybe.MaybeT m)
+ Control.Monad.Catch: instance GHC.Show.Show a => GHC.Show.Show (Control.Monad.Catch.ExitCase a)
+ Control.Monad.Catch: onError :: MonadMask m => m a -> m b -> m a
- Control.Monad.Catch: bracket :: MonadMask m => m a -> (a -> m b) -> (a -> m c) -> m c
+ Control.Monad.Catch: bracket :: MonadMask m => m a -> (a -> m c) -> (a -> m b) -> m b
- Control.Monad.Catch: bracketOnError :: MonadMask m => m a -> (a -> m b) -> (a -> m c) -> m c
+ Control.Monad.Catch: bracketOnError :: MonadMask m => m a -> (a -> m c) -> (a -> m b) -> m b
- Control.Monad.Catch: bracket_ :: MonadMask m => m a -> m b -> m c -> m c
+ Control.Monad.Catch: bracket_ :: MonadMask m => m a -> m c -> m b -> m b
- Control.Monad.Catch: class (Typeable * e, Show e) => Exception e
+ Control.Monad.Catch: class (Typeable e, Show e) => Exception e
- Control.Monad.Catch: data SomeException :: *
+ Control.Monad.Catch: data SomeException
- Control.Monad.Catch: generalBracket :: MonadMask m => m a -> (a -> m ignored1) -> (a -> SomeException -> m ignored2) -> (a -> m b) -> m b
+ Control.Monad.Catch: generalBracket :: MonadMask m => m a -> (a -> ExitCase b -> m c) -> (a -> m b) -> m (b, c)

Files

.travis.yml view
@@ -61,8 +61,8 @@     # env: TEST=--disable-tests BENCH=--disable-benchmarks       addons: {apt: {packages: [*apt_packages,cabal-install-2.0,ghc-8.2.2], sources: [hvr-ghc]}}     - compiler: "ghc-8.4.1"-      env: GHCHEAD=true-      addons: {apt: {packages: [*apt_packages,cabal-install-head,ghc-8.4.1], sources: [hvr-ghc]}}+    # env: TEST=--disable-tests BENCH=--disable-benchmarks+      addons: {apt: {packages: [*apt_packages,cabal-install-2.2,ghc-8.4.1], sources: [hvr-ghc]}}     - compiler: "ghc-head"       env: GHCHEAD=true       addons: {apt: {packages: [*apt_packages,cabal-install-head,ghc-head], sources: [hvr-ghc]}}@@ -70,7 +70,6 @@   allow_failures:     - compiler: "ghc-7.0.4"     - compiler: "ghc-7.2.2"-    - compiler: "ghc-8.4.1"     - compiler: "ghc-head"  before_install:
CHANGELOG.markdown view
@@ -1,3 +1,15 @@+0.10.0+------+* Fix a regression in 0.9.0 whereby the non-IO effects in `bracket`'s `use`+  action were not visible to the `release` action, and the non-IO effects in the+  `release` action were not visible after the `bracket` call.+* The type of `generalBracket` was changed in order to restore those non-IO+  effects, so if you are a library author that provides a `MonadMask` instance,+  you will need to update your implementation of this method.+* Add `MonadMask` instance for `MaybeT`+* Add `onError` function whose action also runs on errors which are not+  exceptions, such as a `Nothing` or a `Left`.+ 0.9.0 ----- * Add `generalBracket` to the `MonadMask` typeclass, allowing more
exceptions.cabal view
@@ -1,6 +1,6 @@ name:          exceptions category:      Control, Exceptions, Monad-version:       0.9.0+version:       0.10.0 cabal-version: >= 1.8 license:       BSD3 license-file:  LICENSE
src/Control/Monad/Catch.hs view
@@ -48,6 +48,7 @@     MonadThrow(..)   , MonadCatch(..)   , MonadMask(..)+  , ExitCase(..)      -- * Utilities     -- $utilities@@ -66,6 +67,7 @@   , try   , tryJust   , onException+  , onError   , bracket   , bracket_   , finally@@ -105,6 +107,10 @@ import Data.Monoid #endif +#if __GLASGOW_HASKELL__ < 710+import Control.Applicative+#endif+ ------------------------------------------------------------------------------ -- $mtl -- The mtl style typeclass@@ -153,17 +159,36 @@ -- asynchronous exceptions. Continuation-based monads are invalid -- instances of this class. ----- Note that this package /does/ provide a @MonadMask@ instance for @CatchT@.--- This instance is /only/ valid if the base monad provides no ability to--- provide multiple exit. For example, @IO@ or @Either@ would be invalid base--- monads, but @Reader@ or @State@ would be acceptable.--- -- Instances should ensure that, in the following code: ----- > f `finally` g+-- > fg = f `finally` g -- -- The action @g@ is called regardless of what occurs within @f@, including--- async exceptions.+-- async exceptions. Some monads allow @f@ to abort the computation via other+-- effects than throwing an exception. For simplicity, we will consider aborting+-- and throwing an exception to be two forms of "throwing an error".+--+-- If @f@ and @g@ both throw an error, the error thrown by @fg@ depends on which+-- errors we're talking about. In a monad transformer stack, the deeper layers+-- override the effects of the inner layers; for example, @ExceptT e1 (Except+-- e2) a@ represents a value of type @Either e2 (Either e1 a)@, so throwing both+-- an @e1@ and an @e2@ will result in @Left e2@. If @f@ and @g@ both throw an+-- error from the same layer, instances should ensure that the error from @g@+-- wins.+--+-- Effects other than throwing an error are also overriden by the deeper layers.+-- For example, @StateT s Maybe a@ represents a value of type @s -> Maybe (a,+-- s)@, so if an error thrown from @f@ causes this function to return @Nothing@,+-- any changes to the state which @f@ also performed will be erased. As a+-- result, @g@ will see the state as it was before @f@. Once @g@ completes,+-- @f@'s error will be rethrown, so @g@' state changes will be erased as well.+-- This is the normal interaction between effects in a monad transformer stack.+--+-- By contrast, <https://hackage.haskell.org/package/lifted-base lifted-base>'s+-- version of 'finally' always discards all of @g@'s non-IO effects, and @g@+-- never sees any of @f@'s non-IO effects, regardless of the layer ordering and+-- regardless of whether @f@ throws an error. This is not the result of+-- interacting effects, but a consequence of @MonadBaseControl@'s approach. class MonadCatch m => MonadMask m where   -- | Runs an action with asynchronous exceptions disabled. The action is   -- provided a method for restoring the async. environment to what it was@@ -178,15 +203,17 @@   -- and/or unkillable.   uninterruptibleMask :: ((forall a. m a -> m a) -> m b) -> m b -  -- | A generalized version of the standard bracket function which allows-  -- distinguishing different exit cases. Instead of providing it a single-  -- release action, this function takes two different actions: one for the-  -- case of a successful run of the inner function, and one in the case of an-  -- exception. The former function is provided the acquired value, while-  -- the exception release function is provided both the acquired value and-  -- the exception that was thrown. The result values of both of these-  -- functions are ignored.+  -- | 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@@ -194,62 +221,98 @@   -- 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. As two examples, here is a @ReaderT@ implementation:+  -- this method. The @StateT@ implementation demonstrates most of the+  -- subtleties:   --   -- @-  -- generalBracket acquire release cleanup use = ReaderT $ \r ->-  --   generalBracket-  --     (runReaderT acquire r)-  --     (\resource -> runReaderT (release resource) r)-  --     (\resource e -> runReaderT (cleanup resource e) r)-  --     (\resource -> runReaderT (use resource) r)+  -- 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)   -- @   ---  -- This implementation reuses the base monad's @generalBracket@, and-  -- simply uses the @ReaderT@ environment to run the relevant-  -- @acquire@, @release@, @cleanup@ (for exceptions), and @use@-  -- actions. A more complicated example is the implementation for-  -- @ExceptT@, which must implement @ExceptT@'s short-circuit logic-  -- itself:+  -- 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 cleanup use = ExceptT $-  --   generalBracket+  -- generalBracket acquire release use = ExceptT $ do+  --   (eb, ec) <- generalBracket   --     (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 ())+  --     (\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 'Left's. We want the error from+  --     -- 'release' to win.+  --     c <- ec+  --     b <- eb+  --     return (b, c)   -- @   ---  -- In this implementation, we need to deal with the potential that-  -- the @acquire@ action returned a @Left@ (as opposed to succeeding-  -- with a @Right@ or throwing an exception via @throwM@), and-  -- therefore have to handle the @Left@ case explicitly when provide-  -- @release@, @cleanup@, and @use@ actions to the base monad's-  -- implementation of @generalBracket@.-  ---  -- You should ensure that in all cases of the @acquire@ action-  -- completing successfully, either the @release@ or @cleanup@-  -- actions are called, regardless of what occurs in @use@.-  --   -- @since 0.9.0   generalBracket     :: m a     -- ^ acquire some resource-    -> (a -> m ignored1)-    -- ^ release, no exception thrown-    -> (a -> SomeException -> m ignored2)-    -- ^ release, some exception thrown; the exception will be rethrown+    -> (a -> ExitCase b -> m c)+    -- ^ release the resource, observing the outcome of the inner action     -> (a -> m b)     -- ^ inner action to perform with the resource-    -> m b+    -> m (b, c) +-- | A 'MonadMask' computation may either succeed with a value, abort with an+-- exception, or abort for some other reason. For example, in @ExceptT e IO@+-- you can use 'throwM' to abort with an exception ('ExitCaseException') or+-- 'Control.Monad.Trans.Except.throwE' to abort with a value of type 'e'+-- ('ExitCaseAbort').+data ExitCase a+  = ExitCaseSuccess a+  | ExitCaseException SomeException+  | ExitCaseAbort+  deriving Show+ instance MonadThrow [] where   throwM _ = [] instance MonadThrow Maybe where@@ -264,13 +327,13 @@ instance MonadMask IO where   mask = ControlException.mask   uninterruptibleMask = ControlException.uninterruptibleMask-  generalBracket acquire release cleanup use = mask $ \unmasked -> do+  generalBracket acquire release use = mask $ \unmasked -> do     resource <- acquire-    result <- unmasked (use resource) `catch` \e -> do-      _ <- cleanup resource e+    b <- unmasked (use resource) `catch` \e -> do+      _ <- release resource (ExitCaseException e)       throwM e-    _ <- release resource-    return result+    c <- release resource (ExitCaseSuccess b)+    return (b, c)  instance MonadThrow STM where   throwM = STM.throwSTM@@ -291,15 +354,15 @@   mask f = f id   uninterruptibleMask f = f id -  generalBracket acquire release cleanup use =+  generalBracket acquire release use =     case acquire of       Left e -> Left e       Right resource ->         case use resource of-          Left e -> cleanup resource e >> Left e-          Right result -> do-            _ <- release resource-            return result+          Left e -> release resource (ExitCaseException e) >> Left e+          Right b -> do+            c <- release resource (ExitCaseSuccess b)+            return (b, c)  instance MonadThrow m => MonadThrow (IdentityT m) where   throwM e = lift $ throwM e@@ -314,11 +377,10 @@       where q :: (m a -> m a) -> IdentityT m a -> IdentityT m a             q u = IdentityT . u . runIdentityT -  generalBracket acquire release cleanup use = IdentityT $+  generalBracket acquire release use = IdentityT $     generalBracket       (runIdentityT acquire)-      (runIdentityT . release)-      (\resource e -> runIdentityT (cleanup resource e))+      (\resource exitCase -> runIdentityT (release resource exitCase))       (\resource -> runIdentityT (use resource))  instance MonadThrow m => MonadThrow (LazyS.StateT s m) where@@ -334,17 +396,20 @@       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+  generalBracket acquire release use = LazyS.StateT $ \s0 -> do+    -- This implementation is given as an example in the documentation of+    -- 'generalBracket', so when changing it, remember to update the+    -- documentation's copy as well+    ((b, _s2), (c, s3)) <- generalBracket       (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) exitCase -> case exitCase of+        ExitCaseSuccess (b, s2) -> LazyS.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     -> LazyS.runStateT (release resource (ExitCaseException e)) s1+        ExitCaseAbort           -> LazyS.runStateT (release resource ExitCaseAbort) s1)       (\(resource, s1) -> LazyS.runStateT (use resource) s1)+    return ((b, c), s3)  instance MonadThrow m => MonadThrow (StrictS.StateT s m) where   throwM e = lift $ throwM e@@ -359,12 +424,17 @@       where q :: (m (a, s) -> m (a, s)) -> StrictS.StateT s m a -> StrictS.StateT s m a             q u (StrictS.StateT b) = StrictS.StateT (u . b) -  generalBracket acquire release cleanup use = StrictS.StateT $ \s0 ->-    generalBracket+  generalBracket acquire release use = StrictS.StateT $ \s0 -> do+    ((b, _s2), (c, s3)) <- generalBracket       (StrictS.runStateT acquire s0)-      (\(resource, s1) -> StrictS.runStateT (release resource) s1)-      (\(resource, s1) e -> StrictS.runStateT (cleanup resource e) s1)+      (\(resource, s1) exitCase -> case exitCase of+        ExitCaseSuccess (b, s2) -> StrictS.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     -> StrictS.runStateT (release resource (ExitCaseException e)) s1+        ExitCaseAbort           -> StrictS.runStateT (release resource ExitCaseAbort) s1)       (\(resource, s1) -> StrictS.runStateT (use resource) s1)+    return ((b, c), s3)  instance MonadThrow m => MonadThrow (ReaderT r m) where   throwM e = lift $ throwM e@@ -379,11 +449,10 @@       where q :: (m a -> m a) -> ReaderT e m a -> ReaderT e m a             q u (ReaderT b) = ReaderT (u . b) -  generalBracket acquire release cleanup use = ReaderT $ \r ->+  generalBracket acquire release use = ReaderT $ \r ->     generalBracket       (runReaderT acquire r)-      (\resource -> runReaderT (release resource) r)-      (\resource e -> runReaderT (cleanup resource e) r)+      (\resource exitCase -> runReaderT (release resource exitCase) r)       (\resource -> runReaderT (use resource) r)  instance (MonadThrow m, Monoid w) => MonadThrow (StrictW.WriterT w m) where@@ -399,18 +468,25 @@       where q :: (m (a, w) -> m (a, w)) -> StrictW.WriterT w m a -> StrictW.WriterT w m a             q u b = StrictW.WriterT $ u (StrictW.runWriterT b) -  generalBracket acquire release cleanup use = StrictW.WriterT $-    generalBracket+  generalBracket acquire release use = StrictW.WriterT $ do+    ((b, _w12), (c, w123)) <- generalBracket       (StrictW.runWriterT acquire)-      -- NOTE: The updated writer values here are actually going to be-      -- lost, as the return value of this cleanup is discarded-      (StrictW.runWriterT . release . fst)-      (\(resource, w1) e -> do-        (a, w2) <- StrictW.runWriterT (cleanup resource e)-        return (a, mappend w1 w2))+      (\(resource, w1) exitCase -> case exitCase of+        ExitCaseSuccess (b, w12) -> do+          (c, w3) <- StrictW.runWriterT (release resource (ExitCaseSuccess b))+          return (c, mappend w12 w3)+        -- In the two other cases, the base monad overrides @use@'s state+        -- changes and the state reverts to @w1@.+        ExitCaseException e -> do+          (c, w3) <- StrictW.runWriterT (release resource (ExitCaseException e))+          return (c, mappend w1 w3)+        ExitCaseAbort -> do+          (c, w3) <- StrictW.runWriterT (release resource ExitCaseAbort)+          return (c, mappend w1 w3))       (\(resource, w1) -> do         (a, w2) <- StrictW.runWriterT (use resource)         return (a, mappend w1 w2))+    return ((b, c), w123)  instance (MonadThrow m, Monoid w) => MonadThrow (LazyW.WriterT w m) where   throwM e = lift $ throwM e@@ -425,16 +501,25 @@       where q :: (m (a, w) -> m (a, w)) -> LazyW.WriterT w m a -> LazyW.WriterT w m a             q u b = LazyW.WriterT $ u (LazyW.runWriterT b) -  generalBracket acquire release cleanup use = LazyW.WriterT $-    generalBracket+  generalBracket acquire release use = LazyW.WriterT $ do+    ((b, _w12), (c, w123)) <- generalBracket       (LazyW.runWriterT acquire)-      (LazyW.runWriterT . release . fst)-      (\(resource, w1) e -> do-        (a, w2) <- LazyW.runWriterT (cleanup resource e)-        return (a, mappend w1 w2))+      (\(resource, w1) exitCase -> case exitCase of+        ExitCaseSuccess (b, w12) -> do+          (c, w3) <- LazyW.runWriterT (release resource (ExitCaseSuccess b))+          return (c, mappend w12 w3)+        -- In the two other cases, the base monad overrides @use@'s state+        -- changes and the state reverts to @w1@.+        ExitCaseException e -> do+          (c, w3) <- LazyW.runWriterT (release resource (ExitCaseException e))+          return (c, mappend w1 w3)+        ExitCaseAbort -> do+          (c, w3) <- LazyW.runWriterT (release resource ExitCaseAbort)+          return (c, mappend w1 w3))       (\(resource, w1) -> do         (a, w2) <- LazyW.runWriterT (use resource)         return (a, mappend w1 w2))+    return ((b, c), w123)  instance (MonadThrow m, Monoid w) => MonadThrow (LazyRWS.RWST r w s m) where   throwM e = lift $ throwM e@@ -449,17 +534,25 @@       where q :: (m (a, s, w) -> m (a, s, w)) -> LazyRWS.RWST r w s m a -> LazyRWS.RWST r w s m a             q u (LazyRWS.RWST b) = LazyRWS.RWST $ \ r s -> u (b r s) -  generalBracket acquire release cleanup use = LazyRWS.RWST $ \r s0 ->-    generalBracket+  generalBracket acquire release use = LazyRWS.RWST $ \r s0 -> do+    ((b, _s2, _w12), (c, s3, w123)) <- generalBracket       (LazyRWS.runRWST acquire r s0)-      -- All comments from StateT and WriterT apply here too-      (\(resource, s1, _) -> LazyRWS.runRWST (release resource) r s1)-      (\(resource, s1, w1) e -> do-        (a, s2, w2) <- LazyRWS.runRWST (cleanup resource e) r s1-        return (a, s2, mappend w1 w2))+      (\(resource, s1, w1) exitCase -> case exitCase of+        ExitCaseSuccess (b, s2, w12) -> do+          (c, s3, w3) <- LazyRWS.runRWST (release resource (ExitCaseSuccess b)) r s2+          return (c, s3, mappend w12 w3)+        -- In the two other cases, the base monad overrides @use@'s state+        -- changes and the state reverts to @s1@ and @w1@.+        ExitCaseException e -> do+          (c, s3, w3) <- LazyRWS.runRWST (release resource (ExitCaseException e)) r s1+          return (c, s3, mappend w1 w3)+        ExitCaseAbort -> do+          (c, s3, w3) <- LazyRWS.runRWST (release resource ExitCaseAbort) r s1+          return (c, s3, mappend w1 w3))       (\(resource, s1, w1) -> do         (a, s2, w2) <- LazyRWS.runRWST (use resource) r s1         return (a, s2, mappend w1 w2))+    return ((b, c), s3, w123)  instance (MonadThrow m, Monoid w) => MonadThrow (StrictRWS.RWST r w s m) where   throwM e = lift $ throwM e@@ -474,16 +567,25 @@       where q :: (m (a, s, w) -> m (a, s, w)) -> StrictRWS.RWST r w s m a -> StrictRWS.RWST r w s m a             q u (StrictRWS.RWST b) = StrictRWS.RWST $ \ r s -> u (b r s) -  generalBracket acquire release cleanup use = StrictRWS.RWST $ \r s0 ->-    generalBracket+  generalBracket acquire release use = StrictRWS.RWST $ \r s0 -> do+    ((b, _s2, _w12), (c, s3, w123)) <- generalBracket       (StrictRWS.runRWST acquire r s0)-      (\(resource, s1, _) -> StrictRWS.runRWST (release resource) r s1)-      (\(resource, s1, w1) e -> do-        (a, s2, w2) <- StrictRWS.runRWST (cleanup resource e) r s1-        return (a, s2, mappend w1 w2))+      (\(resource, s1, w1) exitCase -> case exitCase of+        ExitCaseSuccess (b, s2, w12) -> do+          (c, s3, w3) <- StrictRWS.runRWST (release resource (ExitCaseSuccess b)) r s2+          return (c, s3, mappend w12 w3)+        -- In the two other cases, the base monad overrides @use@'s state+        -- changes and the state reverts to @s1@ and @w1@.+        ExitCaseException e -> do+          (c, s3, w3) <- StrictRWS.runRWST (release resource (ExitCaseException e)) r s1+          return (c, s3, mappend w1 w3)+        ExitCaseAbort -> do+          (c, s3, w3) <- StrictRWS.runRWST (release resource ExitCaseAbort) r s1+          return (c, s3, mappend w1 w3))       (\(resource, s1, w1) -> do         (a, s2, w2) <- StrictRWS.runRWST (use resource) r s1         return (a, s2, mappend w1 w2))+    return ((b, c), s3, w123)  -- Transformers which are only instances of MonadThrow and MonadCatch, not MonadMask instance MonadThrow m => MonadThrow (ListT m) where@@ -497,7 +599,35 @@ -- | Catches exceptions from the base monad. instance MonadCatch m => MonadCatch (MaybeT m) where   catch (MaybeT m) f = MaybeT $ catch m (runMaybeT . f)+-- | @since 0.10.0+instance MonadMask m => MonadMask (MaybeT m) where+  mask f = MaybeT $ mask $ \u -> runMaybeT $ f (q u)+    where+      q :: (m (Maybe a) -> m (Maybe a))+        -> MaybeT m a -> MaybeT m a+      q u (MaybeT b) = MaybeT (u b)+  uninterruptibleMask f = MaybeT $ uninterruptibleMask $ \u -> runMaybeT $ f (q u)+    where+      q :: (m (Maybe a) -> m (Maybe a))+        -> MaybeT m a -> MaybeT m a+      q u (MaybeT b) = MaybeT (u b) +  generalBracket acquire release use = MaybeT $ do+    (eb, ec) <- generalBracket+      (runMaybeT acquire)+      (\resourceMay exitCase -> case resourceMay of+        Nothing -> return Nothing -- nothing to release, acquire didn't succeed+        Just resource -> case exitCase of+          ExitCaseSuccess (Just b) -> runMaybeT (release resource (ExitCaseSuccess b))+          ExitCaseException e      -> runMaybeT (release resource (ExitCaseException e))+          _                        -> runMaybeT (release resource ExitCaseAbort))+      (\resourceMay -> case resourceMay of+        Nothing -> return Nothing+        Just resource -> runMaybeT (use resource))+    -- The order in which we perform those two 'Maybe' effects doesn't matter,+    -- since the error message is the same regardless.+    return ((,) <$> eb <*> ec)+ -- | Throws exceptions into the base monad. instance (Error e, MonadThrow m) => MonadThrow (ErrorT e m) where   throwM = lift . throwM@@ -516,18 +646,23 @@         -> ErrorT e m a -> ErrorT e m a       q u (ErrorT b) = ErrorT (u b) -  generalBracket acquire release cleanup use = ErrorT $-    generalBracket+  generalBracket acquire release use = ErrorT $ do+    (eb, ec) <- generalBracket       (runErrorT acquire)-      (\eresource ->-        case eresource of-          Left _ -> return () -- nothing to release, it didn't succeed-          Right resource -> runErrorT (release resource) >> return ())-      (\eresource e ->-         case eresource of-           Left _ -> return ()-           Right resource -> runErrorT (cleanup resource e) >> return ())+      (\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) -> runErrorT (release resource (ExitCaseSuccess b))+          ExitCaseException e       -> runErrorT (release resource (ExitCaseException e))+          _                         -> runErrorT (release resource ExitCaseAbort))       (either (return . Left) (runErrorT . use))+    return $ do+      -- The order in which we perform those two 'Either' effects determines+      -- which error will win if they are both 'Left's. We want the error from+      -- 'release' to win.+      c <- ec+      b <- eb+      return (b, c)  -- | Throws exceptions into the base monad. instance MonadThrow m => MonadThrow (ExceptT e m) where@@ -535,6 +670,7 @@ -- | Catches exceptions from the base monad. instance MonadCatch m => MonadCatch (ExceptT e m) where   catch (ExceptT m) f = ExceptT $ catch m (runExceptT . f)+-- | @since 0.9.0 instance MonadMask m => MonadMask (ExceptT e m) where   mask f = ExceptT $ mask $ \u -> runExceptT $ f (q u)     where@@ -547,18 +683,26 @@         -> ExceptT e m a -> ExceptT e m a       q u (ExceptT b) = ExceptT (u b) -  generalBracket acquire release cleanup use = ExceptT $-    generalBracket+  generalBracket acquire release use = ExceptT $ do+    -- This implementation is given as an example in the documentation of+    -- 'generalBracket', so when changing it, remember to update the+    -- documentation's copy as well+    (eb, ec) <- generalBracket       (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 ())+      (\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 'Left's. We want the error from+      -- 'release' to win.+      c <- ec+      b <- eb+      return (b, c)  instance MonadThrow m => MonadThrow (ContT r m) where   throwM = lift . throwM@@ -583,6 +727,9 @@  -- | Catches all exceptions, and somewhat defeats the purpose of the extensible -- exception system. Use sparingly.+--+-- /NOTE/ This catches all /exceptions/, but if the monad supports other ways of+-- aborting the computation, those other kinds of errors will not be caught. catchAll :: MonadCatch m => m a -> (SomeException -> m a) -> m a catchAll = catch @@ -653,44 +800,62 @@  -- | Run an action only if an exception is thrown in the main action. The -- exception is not caught, simply rethrown.+--+-- /NOTE/ The action is only run if an /exception/ is thrown. If the monad+-- supports other ways of aborting the computation, the action won't run if+-- those other kinds of errors are thrown. See 'onError'. onException :: MonadCatch m => m a -> m b -> m a onException action handler = action `catchAll` \e -> handler >> throwM e +-- | Run an action only if an error is thrown in the main action. Unlike+-- 'onException', this works with every kind of error, not just exceptions. For+-- example, if @f@ is an 'ExceptT' computation which aborts with a 'Left', the+-- computation @onError f g@ will execute @g@, while @onException f g@ will not.+--+-- This distinction is only meaningful for monads which have multiple exit+-- points, such as 'Except' and 'MaybeT'. For monads that only have a single+-- exit point, there is no difference between 'onException' and 'onError',+-- except that 'onError' has a more constrained type.+--+-- @since 0.10.0+onError :: MonadMask m => m a -> m b -> m a+onError action handler = bracketOnError (return ()) (const handler) (const action)+ -- | Generalized abstracted pattern of safe resource acquisition and release--- in the face of exceptions. The first action \"acquires\" some value, which+-- in the face of errors. The first action \"acquires\" some value, which -- is \"released\" by the second action at the end. The third action \"uses\" -- the value and its result is the result of the 'bracket'. ----- If an exception occurs during the use, the release still happens before the--- exception is rethrown.+-- If an error is thrown during the use, the release still happens before the+-- error is rethrown. -- -- Note that this is essentially a type-specialized version of -- 'generalBracket'. This function has a more common signature (matching the -- signature from "Control.Exception"), and is often more convenient to use. By -- contrast, 'generalBracket' is more expressive, allowing us to implement -- other functions like 'bracketOnError'.-bracket :: MonadMask m => m a -> (a -> m b) -> (a -> m c) -> m c-bracket acquire release use = generalBracket+bracket :: MonadMask m => m a -> (a -> m c) -> (a -> m b) -> m b+bracket acquire release = liftM fst . generalBracket   acquire-  release-  (\a _e -> release a)-  use+  (\a _exitCase -> release a)  -- | Version of 'bracket' without any value being passed to the second and -- third actions.-bracket_ :: MonadMask m => m a -> m b -> m c -> m c+bracket_ :: MonadMask m => m a -> m c -> m b -> m b bracket_ before after action = bracket before (const after) (const action)  -- | Perform an action with a finalizer action that is run, even if an--- exception occurs.+-- error occurs. finally :: MonadMask m => m a -> m b -> m a finally action finalizer = bracket_ (return ()) finalizer action --- | Like 'bracket', but only performs the final action if there was an--- exception raised by the in-between computation.-bracketOnError :: MonadMask m => m a -> (a -> m b) -> (a -> m c) -> m c-bracketOnError acquire release use = generalBracket+-- | Like 'bracket', but only performs the final action if an error is+-- thrown by the in-between computation.+bracketOnError :: MonadMask m => m a -> (a -> m c) -> (a -> m b) -> m b+bracketOnError acquire release = liftM fst . generalBracket   acquire-  (\_ -> return ())-  (\a _e -> release a)-  use+  (\a exitCase -> case exitCase of+    ExitCaseSuccess _ -> return ()+    _ -> do+      _ <- release a+      return ())
src/Control/Monad/Catch/Pure.hs view
@@ -156,22 +156,25 @@     Right a -> return (Right a) -- | Note: This instance is only valid if the underlying monad has a single -- exit point!+--+-- For example, @IO@ or @Either@ would be invalid base monads, but+-- @Reader@ or @State@ would be acceptable. instance Monad m => MonadMask (CatchT m) where   mask a = a id   uninterruptibleMask a = a id-  generalBracket acquire release cleanup use = CatchT $ do+  generalBracket acquire release use = CatchT $ do     eresource <- runCatchT acquire     case eresource of       Left e -> return $ Left e       Right resource -> do-        eresult <- runCatchT (use resource)-        case eresult of-          Left e -> do-            _ <- runCatchT (cleanup resource e)-            return $ Left e-          Right result -> do-            _ <- runCatchT (release resource)-            return $ Right result+        eb <- runCatchT (use resource)+        case eb of+          Left e -> runCatchT $ do+            _ <- release resource (ExitCaseException e)+            throwM e+          Right b -> runCatchT $ do+            c <- release resource (ExitCaseSuccess b)+            return (b, c)  instance MonadState s m => MonadState s (CatchT m) where   get = lift get
tests/Control/Monad/Catch/Tests.hs view
@@ -45,20 +45,30 @@  instance Exception TestException -data MSpec = forall m. (MonadCatch m) => MSpec+data MSpec m = MSpec     { mspecName :: String     , mspecRunner :: (m Property -> Property)     } -testMonadCatch :: MSpec -> Property-testMonadCatch MSpec { mspecRunner } = monadic mspecRunner $+-- the @m Bool@ determines whether the @m ()@ has executed+data DetectableEffect m = DetectableEffect+    { detectableEffectMSpec :: MSpec m+    , detectableEffectEffectDetector :: m (m (), m Bool)+    }++data SomeMSpec = forall m. (MonadCatch m) => SomeMSpec (MSpec m)++data SomeDetectableEffect = forall m. (MonadMask m) => SomeDetectableEffect (DetectableEffect m)++testMonadCatch :: SomeMSpec -> Property+testMonadCatch (SomeMSpec MSpec { mspecRunner }) = monadic mspecRunner $     run $ catch failure handler   where     failure = throwM (TestException "foo") >> error "testMonadCatch"     handler (_ :: TestException) = return () -testCatchJust :: MSpec -> Property-testCatchJust MSpec { mspecRunner } = monadic mspecRunner $ do+testCatchJust :: SomeMSpec -> Property+testCatchJust (SomeMSpec MSpec { mspecRunner }) = monadic mspecRunner $ do     nice <- run $ catchJust testException posFailure posHandler     assert $ nice == ("pos", True)     bad <- run $ catch (catchJust testException negFailure posHandler) negHandler@@ -70,48 +80,153 @@     posFailure = throwM (TestException "pos") >> error "testCatchJust pos"     negFailure = throwM (TestException "neg") >> error "testCatchJust neg" +testDetectableEffect :: SomeDetectableEffect -> Property+testDetectableEffect (SomeDetectableEffect (DetectableEffect mspec effectDetector)) = do+    monadic (mspecRunner mspec) $ do+        effectWasPerformed <- run $ do+          (effect, detector) <- effectDetector+          runExceptT $ ExceptT (return $ Left ()) `finally` lift effect+          detector+        assert effectWasPerformed+ tests :: Test tests = testGroup "Control.Monad.Catch.Tests" $    ([ mkMonadCatch     , mkCatchJust     ] <*> mspecs) +++   ([ mkDetectableEffect+    ] <*> detectableEffects) ++     [ testCase "ExceptT+Left" exceptTLeft+    , testCase "release error wins" releaseErrorWins     ]   where     mspecs =-        [ MSpec "IO" io-        , MSpec "IdentityT IO" $ io . runIdentityT-        , MSpec "LazyState.StateT IO" $ io . flip LazyState.evalStateT ()-        , MSpec "StrictState.StateT IO" $ io . flip StrictState.evalStateT ()-        , MSpec "ReaderT IO" $ io . flip runReaderT ()-        , MSpec "LazyWriter.WriterT IO" $ io . fmap tfst . LazyWriter.runWriterT-        , MSpec "StrictWriter.WriterT IO" $ io . fmap tfst . StrictWriter.runWriterT-        , MSpec "LazyRWS.RWST IO" $ \m -> io $ fmap tfst $ LazyRWS.evalRWST m () ()-        , MSpec "StrictRWS.RWST IO" $ \m -> io $ fmap tfst $ StrictRWS.evalRWST m () ()+        [ SomeMSpec mspecIO+        , SomeMSpec mspecIdentityTIO+        , SomeMSpec mspecLazyStateTIO+        , SomeMSpec mspecStrictStateTIO+        , SomeMSpec mspecReaderTIO+        , SomeMSpec mspecLazyWriterTIO+        , SomeMSpec mspecStrictWriterTIO+        , SomeMSpec mspecLazyRWSTIO+        , SomeMSpec mspecStrictRWSTIO -        , MSpec "ListT IO" $ \m -> io $ fmap (\[x] -> x) (runListT m)-        , MSpec "MaybeT IO" $ \m -> io $ fmap (maybe undefined id) (runMaybeT m)-        , MSpec "ErrorT IO" $ \m -> io $ fmap (either error id) (runErrorT m)-        , MSpec "STM" $ io . atomically-        --, MSpec "ContT IO" $ \m -> io $ runContT m return+        , SomeMSpec mspecListTIO+        , SomeMSpec mspecMaybeTIO+        , SomeMSpec mspecErrorTIO+        , SomeMSpec mspecSTM+        --, SomeMSpec mspecContTIO -        , MSpec "CatchT Identity" $ fromRight . runCatch-        , MSpec "Either SomeException" fromRight+        , SomeMSpec mspecCatchTIdentity+        , SomeMSpec mspecEitherSomeException         ] +    mspecIO :: MSpec IO+    mspecIO = MSpec "IO" io++    mspecIdentityTIO :: MSpec (IdentityT IO)+    mspecIdentityTIO = MSpec "IdentityT IO" $ io . runIdentityT++    mspecLazyStateTIO :: MSpec (LazyState.StateT Bool IO)+    mspecLazyStateTIO = MSpec "LazyState.StateT IO" $ io . flip LazyState.evalStateT False++    mspecStrictStateTIO :: MSpec (StrictState.StateT Bool IO)+    mspecStrictStateTIO = MSpec "StrictState.StateT IO" $ io . flip StrictState.evalStateT False++    mspecReaderTIO :: MSpec (ReaderT () IO)+    mspecReaderTIO = MSpec "ReaderT IO" $ io . flip runReaderT ()++    mspecLazyWriterTIO :: MSpec (LazyWriter.WriterT () IO)+    mspecLazyWriterTIO = MSpec "LazyWriter.WriterT IO" $ io . fmap tfst . LazyWriter.runWriterT++    mspecStrictWriterTIO :: MSpec (StrictWriter.WriterT () IO)+    mspecStrictWriterTIO = MSpec "StrictWriter.WriterT IO" $ io . fmap tfst . StrictWriter.runWriterT++    mspecLazyRWSTIO :: MSpec (LazyRWS.RWST () () Bool IO)+    mspecLazyRWSTIO = MSpec "LazyRWS.RWST IO" $ \m -> io $ fmap tfst $ LazyRWS.evalRWST m () False++    mspecStrictRWSTIO :: MSpec (StrictRWS.RWST () () Bool IO)+    mspecStrictRWSTIO = MSpec "StrictRWS.RWST IO" $ \m -> io $ fmap tfst $ StrictRWS.evalRWST m () False++    mspecListTIO :: MSpec (ListT IO)+    mspecListTIO = MSpec "ListT IO" $ \m -> io $ fmap (\[x] -> x) (runListT m)++    mspecMaybeTIO :: MSpec (MaybeT IO)+    mspecMaybeTIO = MSpec "MaybeT IO" $ \m -> io $ fmap (maybe undefined id) (runMaybeT m)++    mspecErrorTIO :: MSpec (ErrorT String IO)+    mspecErrorTIO = MSpec "ErrorT IO" $ \m -> io $ fmap (either error id) (runErrorT m)++    mspecSTM :: MSpec STM+    mspecSTM = MSpec "STM" $ io . atomically++    --mspecContTIO :: MSpec (ContT () IO)+    --mspecContTIO = MSpec "ContT IO" $ \m -> io $ runContT m return++    mspecCatchTIdentity :: MSpec Catch+    mspecCatchTIdentity = MSpec "Catch" $ fromRight . runCatch++    mspecEitherSomeException :: MSpec (Either SomeException)+    mspecEitherSomeException = MSpec "Either SomeException" fromRight+     tfst :: (Property, ()) -> Property = fst     fromRight (Left _) = error "fromRight"     fromRight (Right a) = a     io = morallyDubiousIOProperty -    mkMonadCatch = mkTestType "MonadCatch" testMonadCatch-    mkCatchJust = mkTestType "catchJust" testCatchJust+    detectableEffects =+        [ SomeDetectableEffect $ detectableEffectIO+        , SomeDetectableEffect detectableEffectLazyStateTIO+        , SomeDetectableEffect detectableEffectStrictStateTIO+        , SomeDetectableEffect detectableEffectLazyRWSTIO+        , SomeDetectableEffect detectableEffectStrictRWSTIO+        ] -    mkTestType name test = \spec ->-        testProperty (name ++ " " ++ mspecName spec) $ once $ test spec+    detectableEffectIO :: DetectableEffect IO+    detectableEffectIO = DetectableEffect mspecIO $ do+      ref <- newIORef False+      return (writeIORef ref True, readIORef ref) +    detectableEffectLazyStateTIO :: DetectableEffect (LazyState.StateT Bool IO)+    detectableEffectLazyStateTIO = DetectableEffect mspecLazyStateTIO $ do+      LazyState.put False+      return (LazyState.put True, LazyState.get)++    detectableEffectStrictStateTIO :: DetectableEffect (StrictState.StateT Bool IO)+    detectableEffectStrictStateTIO = DetectableEffect mspecStrictStateTIO $ do+      StrictState.put False+      return (StrictState.put True, StrictState.get)++    detectableEffectLazyRWSTIO :: DetectableEffect (LazyRWS.RWST () () Bool IO)+    detectableEffectLazyRWSTIO = DetectableEffect mspecLazyRWSTIO $ do+      LazyRWS.put False+      return (LazyRWS.put True, LazyRWS.get)++    detectableEffectStrictRWSTIO :: DetectableEffect (StrictRWS.RWST () () Bool IO)+    detectableEffectStrictRWSTIO = DetectableEffect mspecStrictRWSTIO $ do+      StrictRWS.put False+      return (StrictRWS.put True, StrictRWS.get)++    mkMonadCatch = mkMSpecTest "MonadCatch" testMonadCatch+    mkCatchJust = mkMSpecTest "catchJust" testCatchJust+    mkDetectableEffect = mkDetectableEffectTest "effect during release" testDetectableEffect++    mkMSpecTest :: String -> (SomeMSpec -> Property) -> SomeMSpec -> Test+    mkMSpecTest name test = \someMSpec@(SomeMSpec spec) ->+        testProperty (name ++ " " ++ mspecName spec) $ once $ test someMSpec++    mkDetectableEffectTest :: String -> (SomeDetectableEffect -> Property) -> SomeDetectableEffect -> Test+    mkDetectableEffectTest name test = \someDetectableEffect@(SomeDetectableEffect detectableEffect) ->+        let testName = name ++ " " ++ mspecName (detectableEffectMSpec detectableEffect)+        in testProperty testName $ once $ test someDetectableEffect+     exceptTLeft = do       ref <- newIORef False       Left () <- runExceptT $ ExceptT (return $ Left ()) `finally` lift (writeIORef ref True)       val <- readIORef ref       unless val $ error "Looks like cleanup didn't happen"++    -- if both 'use' and 'release' abort, the 'release' error should win+    releaseErrorWins = do+      Left val <- runExceptT $ ExceptT (return $ Left False) `finally` ExceptT (return $ Left True)+      unless val $ error "Looks like the 'use' error won"