annotated-exception: Exceptions, with checkpoints and context.

[ bsd3, control, library ] [ Propose Tags ]


Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees


  • No Candidates
Versions [RSS],,,,,,,,, (info)
Change log
Dependencies base (>=4.7 && <5), containers, safe-exceptions, text, unliftio-core [details]
License BSD-3-Clause
Copyright 2018 Matt Parsons
Author Matt Parsons
Category Control
Home page
Bug tracker
Source repo head: git clone
Uploaded by parsonsmatt at 2023-09-08T21:15:03Z
Distributions LTSHaskell:, NixOS:, Stackage:
Reverse Dependencies 3 direct, 1 indirect [details]
Downloads 967 total (24 in the last 30 days)
Rating 2.0 (votes: 1) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Docs available [build log]
Last success reported on 2023-09-08 [all 1 reports]

Readme for annotated-exception-

[back to package description]


This library provides a special AnnotatedException type which allows you to decorate Haskell exceptions with additional information. This decoration is totally transparent, and even works with exceptions thrown outside of your application code.

To provide an annotation, you'd use the function checkpoint. This will attach the provided value to any exception that bubbles up through it.

import Control.Exception.Annotated

data MyException = MyException
    deriving (Show, Exception)

main :: IO ()
main = do
    checkpoint "Foo" $ do
        throw MyException

When this program crashes, it will crash with an AnnotatedException that contains the annotation "Foo".

λ> checkpoint "Foo" $ throw MyException
*** Exception: AnnotatedException {annotations = ["Foo"], exception = MyException}

These annotations survive, even if you catch and rethrow with a different exception.

data OtherException = OtherException
    deriving (Show, Exception)

woah :: IO ()
woah = do
        checkpointed =
            checkpoint "Foo" (throw MyException)
        handler MyException =
            throw OtherException


Notice how the checkpoint call doesn't cover the throw OtherException - the exception [Annotation] lives on the thrown exception itself, and this library's catch function ensures that we don't lose that context.

λ> (checkpoint "Foo" (throw MyException)) `catch` \MyException -> throw OtherException
*** Exception: AnnotatedException {annotations = ["Foo"], exception = OtherException}

You can also attach a CallStack to any exception using throwWithCallStack.

Now, you're about to report your exceptions, up near main. We can use try in this module to always get the annotations.

main = do
    eresult <- try $ myProgram
    case eresult of
        Left (AnnotatedException annotations exception) ->
            reportException annotations exception
        Right a ->
            pure a