-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Exceptions, with checkpoints and context. -- -- Please see the README on Github at -- https://github.com/parsonsmatt/annotated-exception#readme @package annotated-exception @version 0.1.2.1 -- | An Annotation is attached to a LocatedException. -- They're essentially a dynamically typed value with a convenient -- IsString instance. I'd recommend using something like -- Data.Aeson.Value or possibly something more strongly typed. module Data.Annotation -- | A wrapper type for putting a CallStack into an -- Annotation. We need this because CallStack does not have -- an Eq instance. newtype CallStackAnnotation CallStackAnnotation :: [(String, SrcLoc)] -> CallStackAnnotation [unCallStackAnnotation] :: CallStackAnnotation -> [(String, SrcLoc)] -- | An Annotation is a wrapper around a value that includes a -- Typeable constraint so we can later unpack it. It is -- essentially a 'Dynamic, but we also include Show and Eq -- so it's more useful. data Annotation [Annotation] :: AnnC a => a -> Annotation -- | The constraints that the value inside an Annotation must have. -- -- We want Typeable so we can do cast and potentially get -- more useful information out of it. type AnnC a = (Typeable a, Eq a, Show a) -- | Wrap a value in an Annotation. toAnnotation :: AnnC a => a -> Annotation -- | Attempt to cast the underlying value out of an -- Annotation. castAnnotation :: forall a. Typeable a => Annotation -> Maybe a -- | Attempt to cast the underlying value out of an -- Annotation. Returns the original Annotation if the cast -- isn't right. tryAnnotation :: forall a. Typeable a => Annotation -> Either a Annotation -- | Attempt to cast list of Annotation into the given type. -- Any Annotation that is not in that form is left untouched. tryAnnotations :: forall a. Typeable a => [Annotation] -> ([a], [Annotation]) -- | Returns the Set of types that are in the given annotations. annotationTypes :: [Annotation] -> Set TypeRep -- | Map a function over the given Annotation. If the types don't -- match up, then the whole thing returns Nothing. mapAnnotation :: (AnnC a, AnnC b) => (a -> b) -> Annotation -> Maybe Annotation -- | Map a function over the Annotation, leaving it unchanged if the -- types don't match. mapMaybeAnnotation :: (AnnC a, AnnC b) => (a -> b) -> Annotation -> Annotation -- | Grab an Annotation corresponding to the CallStack that -- is currently in scope. callStackAnnotation :: HasCallStack => Annotation -- | Stuff a CallStack into an Annotation via the -- CallStackAnnotation newtype wrapper. callStackToAnnotation :: CallStack -> Annotation -- | Attempt to convert an Annotation back into a CallStack. callStackFromAnnotation :: CallStackAnnotation -> CallStack -- | Extract the CallStacks from the [Annotation]. -- Any Annotation not corresponding to a CallStack will be -- in the second element of the tuple. callStackInAnnotations :: [Annotation] -> ([CallStack], [Annotation]) instance GHC.Show.Show Data.Annotation.CallStackAnnotation instance GHC.Classes.Eq Data.Annotation.CallStackAnnotation instance GHC.Classes.Eq Data.Annotation.Annotation instance GHC.Show.Show Data.Annotation.Annotation instance Data.String.IsString Data.Annotation.Annotation -- | This module defines an exception wrapper AnnotatedException -- that carries a list of Annotations, along with some helper -- methods for throwing and catching that can make the annotations -- transparent. -- -- While this library can be used directly, it is recommended that you -- define your own types and functions specific to your domain. As an -- example, checkpoint is useful *only* for providing exception -- annotation information. However, you probably want to use -- checkpoint in concert with other context adding features, like -- logging. -- -- Likewise, the Annotation type defined in Data.Annotation -- is essentially a wrapper for a dynamically typed value. So you -- probably want to define your own checkpoint that uses a custom -- type that you want to enforce throughout your application. module Control.Exception.Annotated -- | The AnnotatedException type wraps an exception with a -- [Annotation]. This can provide a sort of a manual -- stack trace with programmer provided data. data AnnotatedException exception AnnotatedException :: [Annotation] -> exception -> AnnotatedException exception [annotations] :: AnnotatedException exception -> [Annotation] [exception] :: AnnotatedException exception -> exception -- | Attach an empty [Annotation] to an exception. new :: e -> AnnotatedException e -- | Attaches the CallStack to the AnnotatedException that is -- thrown. -- -- The CallStack will *not* be present as a CallStack - it -- will be a CallStackAnnotation. throwWithCallStack :: (HasCallStack, MonadThrow m, Exception e) => e -> m a -- | Add a single Annotation to any exceptions thrown in the -- following action. -- -- Example: -- --
--   main = do
--       checkpoint "Foo" $ do
--           print =<< readFile "I don't exist.markdown"
--   
-- -- The exception thrown due to a missing file will now have an -- Annotation Foo. checkpoint :: MonadCatch m => Annotation -> m a -> m a -- | Add the list of Annotation to any exception thrown in the -- following action. checkpointMany :: MonadCatch m => [Annotation] -> m a -> m a -- | Add the current CallStack to the checkpoint. This function -- searches any thrown exception for a pre-existing CallStack and -- will not overwrite or replace the CallStack if one is already -- present. -- -- Primarily useful when you're wrapping a third party library. checkpointCallStack :: (MonadCatch m, HasCallStack) => m a -> m a -- | Add the current CallStack to the checkpoint. This function -- searches any thrown exception for a pre-existing CallStack and -- will not overwrite or replace the CallStack if one is already -- present. -- -- Primarily useful when you're wrapping a third party library. checkpointCallStackWith :: (MonadCatch m, HasCallStack) => [Annotation] -> m a -> m a -- | Catch an exception. This works just like catch, but it also -- will attempt to catch AnnotatedException e. The -- annotations will be preserved in the handler, so rethrowing exceptions -- will retain the context. -- -- Let's consider a few examples, that share this import and exception -- type. -- --
--   import qualified Control.Exception.Safe as Safe
--   import Control.Exception.Annotated
--   
--   data TestException deriving (Show, Exception)
--   
-- -- We can throw an exception and catch it as usual. -- --
--   throw TestException `catch` \TestException ->
--       putStrLn "ok!"
--   
-- -- We can throw an exception and catch it with location. -- --
--   throw TestException `catch` \(AnnotatedException anns TestException) ->
--       putStrLn "ok!"
--   
-- -- We can throw an exception and catch it as a -- AnnotatedException SomeException. -- --
--   throw TestException `catch` \(AnnotatedException anns (e :: SomeException) ->
--       putStrLn "ok!"
--   
catch :: (Exception e, MonadCatch m) => m a -> (e -> m a) -> m a -- | Like catches, but this function enhance the provided -- Handlers to "see through" any AnnotatedExceptions. catches :: MonadCatch m => m a -> [Handler m a] -> m a -- | Like catch, but always returns a AnnotatedException. tryAnnotated :: (Exception e, MonadCatch m) => m a -> m (Either (AnnotatedException e) a) -- | Like try, but can also handle an AnnotatedException or -- the underlying value. Useful when you want to try to catch a -- type of exception, but you may not care about the Annotations -- that it may or may not have. -- -- Example: -- --
--   Left exn <- try $ throw (AnnotatedException [] TestException)
--   exn == TestException
--   
-- --
--   Left exn <- try $ throw TestException
--   exn == AnnotatedException [] TestException
--   
try :: (Exception e, MonadCatch m) => m a -> m (Either e a) -- | Call fromException on the underlying Exception, -- attaching the annotations to the result. check :: Exception e => AnnotatedException SomeException -> Maybe (AnnotatedException e) -- | Call toException on the underlying Exception. hide :: Exception e => AnnotatedException e -> AnnotatedException SomeException -- | Retrieves the CallStack from an AnnotatedException if -- one is present. annotatedExceptionCallStack :: AnnotatedException exception -> Maybe CallStack -- | Adds a CallStack to the given AnnotatedException. This -- function will search through the existing annotations, and it will not -- add a second CallStack to the list. addCallStackToException :: CallStack -> AnnotatedException exception -> AnnotatedException exception -- | An Annotation is a wrapper around a value that includes a -- Typeable constraint so we can later unpack it. It is -- essentially a 'Dynamic, but we also include Show and Eq -- so it's more useful. data Annotation [Annotation] :: AnnC a => a -> Annotation -- | A wrapper type for putting a CallStack into an -- Annotation. We need this because CallStack does not have -- an Eq instance. newtype CallStackAnnotation CallStackAnnotation :: [(String, SrcLoc)] -> CallStackAnnotation [unCallStackAnnotation] :: CallStackAnnotation -> [(String, SrcLoc)] -- | Any type that you wish to throw or catch as an exception must be an -- instance of the Exception class. The simplest case is a new -- exception type directly below the root: -- --
--   data MyException = ThisException | ThatException
--       deriving Show
--   
--   instance Exception MyException
--   
-- -- The default method definitions in the Exception class do what -- we need in this case. You can now throw and catch -- ThisException and ThatException as exceptions: -- --
--   *Main> throw ThisException `catch` \e -> putStrLn ("Caught " ++ show (e :: MyException))
--   Caught ThisException
--   
-- -- In more complicated examples, you may wish to define a whole hierarchy -- of exceptions: -- --
--   ---------------------------------------------------------------------
--   -- Make the root exception type for all the exceptions in a compiler
--   
--   data SomeCompilerException = forall e . Exception e => SomeCompilerException e
--   
--   instance Show SomeCompilerException where
--       show (SomeCompilerException e) = show e
--   
--   instance Exception SomeCompilerException
--   
--   compilerExceptionToException :: Exception e => e -> SomeException
--   compilerExceptionToException = toException . SomeCompilerException
--   
--   compilerExceptionFromException :: Exception e => SomeException -> Maybe e
--   compilerExceptionFromException x = do
--       SomeCompilerException a <- fromException x
--       cast a
--   
--   ---------------------------------------------------------------------
--   -- Make a subhierarchy for exceptions in the frontend of the compiler
--   
--   data SomeFrontendException = forall e . Exception e => SomeFrontendException e
--   
--   instance Show SomeFrontendException where
--       show (SomeFrontendException e) = show e
--   
--   instance Exception SomeFrontendException where
--       toException = compilerExceptionToException
--       fromException = compilerExceptionFromException
--   
--   frontendExceptionToException :: Exception e => e -> SomeException
--   frontendExceptionToException = toException . SomeFrontendException
--   
--   frontendExceptionFromException :: Exception e => SomeException -> Maybe e
--   frontendExceptionFromException x = do
--       SomeFrontendException a <- fromException x
--       cast a
--   
--   ---------------------------------------------------------------------
--   -- Make an exception type for a particular frontend compiler exception
--   
--   data MismatchedParentheses = MismatchedParentheses
--       deriving Show
--   
--   instance Exception MismatchedParentheses where
--       toException   = frontendExceptionToException
--       fromException = frontendExceptionFromException
--   
-- -- We can now catch a MismatchedParentheses exception as -- MismatchedParentheses, SomeFrontendException or -- SomeCompilerException, but not other types, e.g. -- IOException: -- --
--   *Main> throw MismatchedParentheses `catch` \e -> putStrLn ("Caught " ++ show (e :: MismatchedParentheses))
--   Caught MismatchedParentheses
--   *Main> throw MismatchedParentheses `catch` \e -> putStrLn ("Caught " ++ show (e :: SomeFrontendException))
--   Caught MismatchedParentheses
--   *Main> throw MismatchedParentheses `catch` \e -> putStrLn ("Caught " ++ show (e :: SomeCompilerException))
--   Caught MismatchedParentheses
--   *Main> throw MismatchedParentheses `catch` \e -> putStrLn ("Caught " ++ show (e :: IOException))
--   *** Exception: MismatchedParentheses
--   
class (Typeable e, Show e) => Exception e toException :: Exception e => e -> SomeException fromException :: Exception e => SomeException -> Maybe e -- | Render this exception value in a human-friendly manner. -- -- Default implementation: show. displayException :: Exception e => e -> String -- | The SomeException type is the root of the exception type -- hierarchy. When an exception of type e is thrown, behind the -- scenes it is encapsulated in a SomeException. data SomeException SomeException :: e -> SomeException -- | Synchronously throw the given exception throw :: (MonadThrow m, Exception e) => e -> m a -- | Generalized version of Handler data Handler (m :: Type -> Type) a Handler :: (e -> m a) -> Handler (m :: Type -> Type) a instance Data.Traversable.Traversable Control.Exception.Annotated.AnnotatedException instance Data.Foldable.Foldable Control.Exception.Annotated.AnnotatedException instance GHC.Base.Functor Control.Exception.Annotated.AnnotatedException instance GHC.Show.Show exception => GHC.Show.Show (Control.Exception.Annotated.AnnotatedException exception) instance GHC.Classes.Eq exception => GHC.Classes.Eq (Control.Exception.Annotated.AnnotatedException exception) instance GHC.Base.Applicative Control.Exception.Annotated.AnnotatedException instance GHC.Exception.Type.Exception exception => GHC.Exception.Type.Exception (Control.Exception.Annotated.AnnotatedException exception) -- | This module presents the same interface as -- Control.Exception.Annotated, but uses MonadUnliftIO -- instead of MonadCatch or MonadThrow. module Control.Exception.Annotated.UnliftIO -- | The AnnotatedException type wraps an exception with a -- [Annotation]. This can provide a sort of a manual -- stack trace with programmer provided data. data AnnotatedException exception AnnotatedException :: [Annotation] -> exception -> AnnotatedException exception [annotations] :: AnnotatedException exception -> [Annotation] [exception] :: AnnotatedException exception -> exception -- | Attach an empty [Annotation] to an exception. new :: e -> AnnotatedException e -- | Like throwWithCallStack, but uses MonadIO instead of -- MonadThrow. throwWithCallStack :: forall e m a. (MonadIO m, Exception e, HasCallStack) => e -> m a -- | Like checkpoint, but uses MonadUnliftIO instead of -- MonadCatch. checkpoint :: forall m a. MonadUnliftIO m => Annotation -> m a -> m a -- | Like checkpointMany, but uses MonadUnliftIO instead of -- MonadCatch. checkpointMany :: forall m a. MonadUnliftIO m => [Annotation] -> m a -> m a -- | Add the current CallStack to the checkpoint. This function -- searches any thrown exception for a pre-existing CallStack and -- will not overwrite or replace the CallStack if one is already -- present. -- -- Primarily useful when you're wrapping a third party library. checkpointCallStack :: (MonadCatch m, HasCallStack) => m a -> m a -- | Like checkpointCallStackWith, but uses MonadUnliftIO -- instead of MonadCatch. checkpointCallStackWith :: forall m a. (MonadUnliftIO m, HasCallStack) => [Annotation] -> m a -> m a -- | Like catch, but uses MonadUnliftIO instead of -- MonadCatch. catch :: forall e m a. (MonadUnliftIO m, Exception e) => m a -> (e -> m a) -> m a -- | Like catches, bt uses MonadUnliftIO instead of -- MonadCatch. catches :: forall m a. MonadUnliftIO m => m a -> [Handler m a] -> m a -- | Like tryAnnotated but uses MonadUnliftIO instead of -- MonadCatch. tryAnnotated :: forall e m a. (MonadUnliftIO m, Exception e) => m a -> m (Either (AnnotatedException e) a) -- | Like try but uses MonadUnliftIO instead of -- MonadCatch. try :: forall e m a. (MonadUnliftIO m, Exception e) => m a -> m (Either e a) -- | Call fromException on the underlying Exception, -- attaching the annotations to the result. check :: Exception e => AnnotatedException SomeException -> Maybe (AnnotatedException e) -- | Call toException on the underlying Exception. hide :: Exception e => AnnotatedException e -> AnnotatedException SomeException -- | Retrieves the CallStack from an AnnotatedException if -- one is present. annotatedExceptionCallStack :: AnnotatedException exception -> Maybe CallStack -- | Adds a CallStack to the given AnnotatedException. This -- function will search through the existing annotations, and it will not -- add a second CallStack to the list. addCallStackToException :: CallStack -> AnnotatedException exception -> AnnotatedException exception -- | An Annotation is a wrapper around a value that includes a -- Typeable constraint so we can later unpack it. It is -- essentially a 'Dynamic, but we also include Show and Eq -- so it's more useful. data Annotation [Annotation] :: AnnC a => a -> Annotation -- | A wrapper type for putting a CallStack into an -- Annotation. We need this because CallStack does not have -- an Eq instance. newtype CallStackAnnotation CallStackAnnotation :: [(String, SrcLoc)] -> CallStackAnnotation [unCallStackAnnotation] :: CallStackAnnotation -> [(String, SrcLoc)] -- | Any type that you wish to throw or catch as an exception must be an -- instance of the Exception class. The simplest case is a new -- exception type directly below the root: -- --
--   data MyException = ThisException | ThatException
--       deriving Show
--   
--   instance Exception MyException
--   
-- -- The default method definitions in the Exception class do what -- we need in this case. You can now throw and catch -- ThisException and ThatException as exceptions: -- --
--   *Main> throw ThisException `catch` \e -> putStrLn ("Caught " ++ show (e :: MyException))
--   Caught ThisException
--   
-- -- In more complicated examples, you may wish to define a whole hierarchy -- of exceptions: -- --
--   ---------------------------------------------------------------------
--   -- Make the root exception type for all the exceptions in a compiler
--   
--   data SomeCompilerException = forall e . Exception e => SomeCompilerException e
--   
--   instance Show SomeCompilerException where
--       show (SomeCompilerException e) = show e
--   
--   instance Exception SomeCompilerException
--   
--   compilerExceptionToException :: Exception e => e -> SomeException
--   compilerExceptionToException = toException . SomeCompilerException
--   
--   compilerExceptionFromException :: Exception e => SomeException -> Maybe e
--   compilerExceptionFromException x = do
--       SomeCompilerException a <- fromException x
--       cast a
--   
--   ---------------------------------------------------------------------
--   -- Make a subhierarchy for exceptions in the frontend of the compiler
--   
--   data SomeFrontendException = forall e . Exception e => SomeFrontendException e
--   
--   instance Show SomeFrontendException where
--       show (SomeFrontendException e) = show e
--   
--   instance Exception SomeFrontendException where
--       toException = compilerExceptionToException
--       fromException = compilerExceptionFromException
--   
--   frontendExceptionToException :: Exception e => e -> SomeException
--   frontendExceptionToException = toException . SomeFrontendException
--   
--   frontendExceptionFromException :: Exception e => SomeException -> Maybe e
--   frontendExceptionFromException x = do
--       SomeFrontendException a <- fromException x
--       cast a
--   
--   ---------------------------------------------------------------------
--   -- Make an exception type for a particular frontend compiler exception
--   
--   data MismatchedParentheses = MismatchedParentheses
--       deriving Show
--   
--   instance Exception MismatchedParentheses where
--       toException   = frontendExceptionToException
--       fromException = frontendExceptionFromException
--   
-- -- We can now catch a MismatchedParentheses exception as -- MismatchedParentheses, SomeFrontendException or -- SomeCompilerException, but not other types, e.g. -- IOException: -- --
--   *Main> throw MismatchedParentheses `catch` \e -> putStrLn ("Caught " ++ show (e :: MismatchedParentheses))
--   Caught MismatchedParentheses
--   *Main> throw MismatchedParentheses `catch` \e -> putStrLn ("Caught " ++ show (e :: SomeFrontendException))
--   Caught MismatchedParentheses
--   *Main> throw MismatchedParentheses `catch` \e -> putStrLn ("Caught " ++ show (e :: SomeCompilerException))
--   Caught MismatchedParentheses
--   *Main> throw MismatchedParentheses `catch` \e -> putStrLn ("Caught " ++ show (e :: IOException))
--   *** Exception: MismatchedParentheses
--   
class (Typeable e, Show e) => Exception e toException :: Exception e => e -> SomeException fromException :: Exception e => SomeException -> Maybe e -- | Render this exception value in a human-friendly manner. -- -- Default implementation: show. displayException :: Exception e => e -> String -- | The SomeException type is the root of the exception type -- hierarchy. When an exception of type e is thrown, behind the -- scenes it is encapsulated in a SomeException. data SomeException SomeException :: e -> SomeException -- | Like throw, but uses MonadIO instead of -- MonadThrow. throw :: forall e m a. (MonadIO m, Exception e) => e -> m a -- | Generalized version of Handler data Handler (m :: Type -> Type) a Handler :: (e -> m a) -> Handler (m :: Type -> Type) a -- | Monads in which IO computations may be embedded. Any monad -- built by applying a sequence of monad transformers to the IO -- monad will be an instance of this class. -- -- Instances should satisfy the following laws, which state that -- liftIO is a transformer of monads: -- -- class Monad m => MonadIO (m :: Type -> Type) -- | Lift a computation from the IO monad. liftIO :: MonadIO m => IO a -> m a -- | Monads which allow their actions to be run in IO. -- -- While MonadIO allows an IO action to be lifted into -- another monad, this class captures the opposite concept: allowing you -- to capture the monadic context. Note that, in order to meet the laws -- given below, the intuition is that a monad must have no monadic state, -- but may have monadic context. This essentially limits -- MonadUnliftIO to ReaderT and IdentityT -- transformers on top of IO. -- -- Laws. For any value u returned by askUnliftIO, it must -- meet the monad transformer laws as reformulated for -- MonadUnliftIO: -- -- -- -- Instances of MonadUnliftIO must also satisfy the idempotency -- law: -- -- -- -- This law showcases two properties. First, askUnliftIO doesn't -- change the monadic context, and second, liftIO . unliftIO u -- is equivalent to id IF called in the same monadic context as -- askUnliftIO. class MonadIO m => MonadUnliftIO (m :: Type -> Type) -- | Convenience function for capturing the monadic context and running an -- IO action with a runner function. The runner function is used -- to run a monadic action m in IO. withRunInIO :: MonadUnliftIO m => ((forall a. () => m a -> IO a) -> IO b) -> m b