packages feed

safe-exceptions 0.1.4.0 → 0.1.5.0

raw patch · 5 files changed

+95/−41 lines, 5 filesdep ~base

Dependency ranges changed: base

Files

ChangeLog.md view
@@ -1,3 +1,8 @@+## 0.1.5.0++* Re-export `Control.Exception.assert`+* Add `throwString`+ ## 0.1.4.0  * Add `catchJust`, `handleJust`, and `tryJust`
README.md view
@@ -2,19 +2,18 @@  *Safe, consistent, and easy exception handling* -__NOTE__: This library is early in its development, and there may be some-changes in the near future. See [possible future-changes](#possible-future-changes).- [![Build Status](https://travis-ci.org/fpco/safe-exceptions.svg?branch=master)](https://travis-ci.org/fpco/safe-exceptions)  Runtime exceptions - as exposed in `base` by the `Control.Exception` module - have long been an intimidating part of the Haskell ecosystem. This package, and this README for the package, are intended-to overcome this. By providing an API that encourages best practices,-and explaining the corner cases clearly, the hope is to turn what was-previously something scary into an aspect of Haskell everyone feels-safe using.+to overcome this. It provides a safe and simple API on top of the+existing exception handling machinery. The API is equivalent to the+underlying implementation in terms of power but encourages best+practices to minimize the chances of getting the exception handling+wrong.  By doing so and explaining the corner cases clearly, the hope is+to turn what was previously something scary into an aspect of Haskell+everyone feels safe using.  ## Goals @@ -71,6 +70,9 @@ Hopefully this will be able to get you up-and-running quickly. You may also be interested in [browsing through the cookbook](https://github.com/fpco/safe-exceptions/blob/master/COOKBOOK.md).+There is also an+[exception safety tutorial on haskell-lang.org](https://haskell-lang.org/tutorial/exception-safety)+which is based on this package.  ## Terminology @@ -165,8 +167,8 @@ Therefore, this package takes the approach of trusting type information to determine if an exception is asynchronous or synchronous. The details are less interesting to a user, but the-basics are: we leverage the extensible extension system in GHC and-state that any extension type which is a child of `SomeAsyncException`+basics are: we leverage the extensible exception system in GHC and+state that any exception type which is a child of `SomeAsyncException` is an async exception. All other exception types are assumed to be synchronous. @@ -321,30 +323,3 @@   kill signal from another thread or the user (via Ctrl-C), we would   like to be able to detect that we entered a deadlock condition and do   something intelligent in an application.--## Possible future changes--### Interruptible vs uninterruptible masking--This discussion is now being tracked at:-https://github.com/fpco/safe-exceptions/issues/3--In `Control.Exception`, allocation functions and cleanup handlers in-combinators like `bracket` are masked using the (interruptible) `mask`-function, in contrast to `uninterruptibleMask`. There have been some debates-about the correctness of this in the past, notably [a libraries mailing list-discussion kicked off by Eyal-Lotem](https://mail.haskell.org/pipermail/libraries/2014-September/023675.html).-It seems that general consensus is:--* `uninterruptibleMask` is a better choice-* But changing the core library like this would potentially break too many-  programs--In its current version, this library uses `mask` (interruptible) for allocation-functions and `uninterruptibleMask` cleanup handlers. This is a debatable-decision (and one worth debating!). An example of alternatives would be:--* Use `uninterruptibleMask` for both allocation and cleanup pieces-* Match `Control.Exception`'s behavior-* Provide two versions of each function, or possibly two modules
safe-exceptions.cabal view
@@ -1,5 +1,5 @@ name:                safe-exceptions-version:             0.1.4.0+version:             0.1.5.0 synopsis:            Safe, consistent, and easy exception handling description:         Please see README.md homepage:            https://github.com/fpco/safe-exceptions#readme
src/Control/Exception/Safe.hs view
@@ -2,6 +2,7 @@ {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE ImplicitParams #-} -- | Please see the README.md file in the safe-exceptions repo for -- information on how to use this module. Relevant links: --@@ -13,6 +14,8 @@       throw     , throwIO     , throwM+    , throwString+    , StringException (..)     , throwTo     , impureThrow       -- * Catching (with recovery)@@ -77,6 +80,7 @@     , SomeException (..)     , SomeAsyncException (..)     , E.IOException+    , E.assert #if !MIN_VERSION_base(4,8,0)     , displayException #endif@@ -91,6 +95,11 @@ import Control.Monad.IO.Class (MonadIO, liftIO) import Data.Typeable (Typeable, cast) +#if MIN_VERSION_base(4,9,0)+import GHC.Stack (prettySrcLoc)+import GHC.Stack.Types (HasCallStack, CallStack, getCallStack)+#endif+ -- | Synchronously throw the given exception -- -- @since 0.1.0.0@@ -109,8 +118,65 @@ throwM :: (C.MonadThrow m, Exception e) => e -> m a throwM = throw --- | Throw an asynchronous exception to another thread+-- | A convenience function for throwing a user error. This is useful+-- for cases where it would be too high a burden to define your own+-- exception type. --+-- This throws an exception of type 'StringException'. When GHC+-- supports it (base 4.9 and GHC 8.0 and onward), it includes a call+-- stack.+--+-- @since 0.1.5.0+#if MIN_VERSION_base(4,9,0)+throwString :: (C.MonadThrow m, HasCallStack) => String -> m a+throwString s = throwM (StringException s ?callStack)+#else+throwString :: C.MonadThrow m => String -> m a+throwString s = throwM (StringException s ())+#endif++-- | Exception type thrown by 'throwString'.+--+-- Note that the second field of the data constructor depends on+-- GHC/base version. For base 4.9 and GHC 8.0 and later, the second+-- field is a call stack. Previous versions of GHC and base do not+-- support call stacks, and the field is simply unit (provided to make+-- pattern matching across GHC versions easier).+--+-- @since 0.1.5.0+#if MIN_VERSION_base(4,9,0)+data StringException = StringException String CallStack+  deriving Typeable++instance Show StringException where+    show (StringException s cs) = concat+        $ "Control.Exception.Safe.throwString called with:\n\n"+        : s+        : "\nCalled from:\n"+        : map go (getCallStack cs)+      where+        go (x, y) = concat+          [ "  "+          , x+          , " ("+          , prettySrcLoc y+          , ")\n"+          ]+#else+data StringException = StringException String ()+  deriving Typeable++instance Show StringException where+    show (StringException s _) = "Control.Exception.Safe.throwString called with:\n\n" ++ s+#endif+instance Exception StringException++-- | Throw an asynchronous exception to another thread.+--+-- Synchronously typed exceptions will be wrapped into an+-- `AsyncExceptionWrapper`, see+-- <https://github.com/fpco/safe-exceptions#determining-sync-vs-async>+-- -- It's usually a better idea to use the async package, see -- <https://github.com/fpco/safe-exceptions#quickstart> --@@ -325,7 +391,7 @@                 C.try $ C.uninterruptibleMask_ $ after x             C.throwM e1         Right y -> do-            C.uninterruptibleMask_ $ after x+            _ <- C.uninterruptibleMask_ $ after x             return y  -- | Async safe version of 'E.bracket_'@@ -346,7 +412,7 @@             _ :: Either SomeException b <- C.try after             C.throwM e1         Right x -> do-            after+            _ <- after             return x  -- | Async safe version of 'E.bracketOnError'
test/Control/Exception/SafeSpec.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE CPP #-} {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE ScopedTypeVariables #-} module Control.Exception.SafeSpec (spec) where@@ -12,6 +13,9 @@ import System.IO.Unsafe (unsafePerformIO) import System.Timeout (timeout) import Test.Hspec+#if !MIN_VERSION_base(4,9,0)+import System.IO.Error (isUserError)+#endif  newtype ExceptionPred = ExceptionPred { getExceptionPred :: Maybe () } deriving (Show, Eq, Typeable) @@ -124,3 +128,7 @@         let ex = ExceptionPred Nothing         res <- try (catchJust getExceptionPred (throw ex) (return . Just))         res `shouldBe` Left ex++    describe "throwString" $ do+      it "is a StringException" $+        throwString "foo" `catch` \(StringException _ _) -> return () :: IO ()