-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Combinators for handling errors of many types in a composable way -- -- Combinators for handling errors of many types in a composable way. @package oops @version 0.2.0.1 -- | Traditionally in Haskell, we use Either a b to represent a -- choice of two types. If we want to represent three types, we -- use Either a (Either b c), and this nesting can continue as -- far as it needs to. However, this approach comes with some -- difficulties: it's quite difficult to manipulate, and makes for some -- rather unwieldy type signatures. -- -- Thankfully, though, GHC provides us with GADTs, and they allow us to -- construct a type that encompasses a coproduct of any number of -- arguments: the Variant. Just as Left 3 and Right -- True are of type Either Int Bool, we can write Here -- 3 and There (Here True) to do the same thing (ignoring -- Identity wrappers). We can think of the Here and -- There constructors as an "index": the index of the type we're -- storing is the number of occurrences of There. -- -- $setup >>> :set -XTypeOperators -XDataKinds -- -XTypeApplications -- --
--   > > :t [ Here (Identity 'a'), There (There (Here (Identity True))) ]
--   
-- -- module Data.Variant -- | The type VariantF f '[x, y, z] is either f x, -- f y, or f z. The We construct these with -- Here, There . Here, and There . There . -- Here respectively, and we can think o fthe number of -- There-nestings as being the index of our chosen type in the -- type-level list of options. -- -- Often, however, we'll want to avoid being too explicit about our list -- of types, preferring instead to describe it with constraints. See the -- methods below for more information! -- --
--   > > :t [ Here (pure "Hello"), There (Here (pure True)) ]
--   
-- -- data VariantF (f :: k -> Type) (xs :: [k]) [Here] :: f x -> VariantF f (x : xs) [There] :: VariantF f xs -> VariantF f (x : xs) -- | Often, you'll want to have a choice of types that aren't all -- wrapped in a functor. For this, we provide the Variant type -- synonym, as well as equivalents of all the functions below. These -- functions take care of wrapping and unwrapping the Identity -- wrapper, too, so it should be invisible to users. type Variant (xs :: [Type]) = VariantF Identity xs -- | Remove the first possibility from a variant. One nice possibility here -- is a function that tells us whether the first type was the one in our -- variant: variantF Left Right. For example: -- --
--   >>> :set -XDataKinds
--   
--   >>> variantF Left Right (Here (Identity True) :: Variant '[Bool])
--   Left (Identity True)
--   
-- --
--   >>> variantF Left Right (There (Here (Identity 3)) :: Variant '[Bool, Int])
--   Right (Here (Identity 3))
--   
variantF :: (f x -> r) -> (VariantF f xs -> r) -> VariantF f (x : xs) -> r -- | Same as VariantF, but the value will be unwrapped (not in -- Identity) if found. -- --
--   >>> variant Left Right (Here (Identity True) :: Variant '[Bool])
--   Left True
--   
-- --
--   >>> variant Left Right (There (Here (Identity 3)) :: Variant '[Bool, Int])
--   Right (Here (Identity 3))
--   
variant :: (x -> r) -> (Variant xs -> r) -> Variant (x : xs) -> r -- | Same as caseF, but without the functor wrappers. Again, this -- function will specialise according to the provided variant: -- --
--   > > :t case_ (throw True :: Variant '[Bool, Int])
--   
-- -- case_ (throw True :: Variant '[Bool, Int]) :: (Bool -> o) -> -- (Int -> o) -> o -- -- You can also use TypeApplications to check the specialisation -- for a particular variant: -- --
--   > > :t case_ @'[Int, Bool, String]
--   
-- -- case_ @'[Int, Bool, String] :: Variant '[Int, Bool, String] -> (Int -- -> o) -> (Bool -> o) -> ([Char] -> o) -> o case_ :: Case xs r fold => Variant xs -> fold -- | The either function provides us with a way of folding an -- Either by providing a function for each possible constructor: -- Left and Right. In our case, we could have any number of -- functions to supply, depending on how many types are in our type-level -- index. -- -- This function specialises depending on the variant provided: -- --
--   > > :t caseF (throw True :: Variant '[Bool])
--   
-- -- caseF (throw True :: Variant '[Bool]) :: (Identity Bool -> r) -> -- r -- --
--   > > :t caseF (throwF (pure True) :: VariantF IO '[Int, Bool])
--   
-- -- caseF (throwF (pure True) :: VariantF IO '[Int, Bool]) :: (IO Int -- -> o) -> (IO Bool -> o) -> o caseF :: CaseF xs f r fold => VariantF f xs -> fold -- | When dealing with larger (or polymorphic) variants, it becomes -- difficult (or impossible) to construct VariantF values -- explicitly. In that case, the throwF function gives us a -- polymorphic way to lift values into variants. -- --
--   >>> throwF (pure "Hello") :: VariantF Maybe '[Bool, Int, Double, String]
--   There (There (There (Here (Just "Hello"))))
--   
-- --
--   >>> throwF (pure True) :: VariantF Maybe '[Bool, Int, Double, String]
--   Here (Just True)
--   
-- --
--   >>> throwF (pure True) :: VariantF IO '[Int, Double, String]
--   ...
--   ... • Uh oh! I couldn't find Bool inside the variant!
--   ...   If you're pretty sure I'm wrong, perhaps the variant type is ambiguous;
--   ...   could you add some annotations?
--   ...
--   
class CouldBeF (xs :: [k]) (x :: k) throwF :: CouldBeF xs x => f x -> VariantF f xs snatchF :: CouldBeF xs x => VariantF f xs -> Either (VariantF f xs) (f x) -- | Just as with CouldBeF, we can "throw" values not in a -- functor context into a regular Variant. -- --
--   >>> throw (3 :: Int) :: Variant '[Bool, Int, Double, String]
--   There (Here (Identity 3))
--   
-- --
--   >>> throw "Woo!" :: Variant '[Bool, Int, Double, String]
--   There (There (There (Here (Identity "Woo!"))))
--   
class CouldBeF xs x => CouldBe (xs :: [Type]) (x :: Type) throw :: CouldBe xs x => x -> Variant xs snatch :: CouldBe xs x => Variant xs -> Either (Variant xs) x -- | As with CouldBeAnyOf, we can also constrain a variant to -- represent several possible types, as we might with several -- CouldBeF constraints, using one type-level list. type e `CouldBeAnyOfF` xs = All (Map (CouldBeF e) xs) -- | Listing larger variants' constraints might amplify the noise of -- functions' signatures. The CouldBeAnyOfF constraint lets us -- specify several types a variant may contain in a single type-level -- list, as opposed to several independent constraints. So, we could -- replace, -- -- f :: (e CouldBe Int, e CouldBe Bool, e CouldBe -- Char) => VariantF IO e -- -- with the equivalent constraint, -- -- f :: e CouldBeAnyOf '[Int, Bool, Char] => VariantF IO e -- -- As CouldBeAnyOf is just short-hand, we can use throw -- just like when we have CouldBe constraints: -- --
--   >>> :set -XTypeOperators
--   
--   >>> :{
--   f :: e `CouldBeAnyOf` '[Int, Bool, Char] => Variant e
--   f = throw 'c'
--   :}
--   
-- -- ... and eliminate constraints in just the same way: -- --
--   >>> :{
--   g :: e `CouldBeAnyOf` '[Int, Bool] => Either (Variant e) Char
--   g = catch @Char f
--   :}
--   
type e `CouldBeAnyOf` xs = All (Map (CouldBe e) xs) -- | This is an odd constraint, as you should rarely need to see it. -- GHC's partial instantiation tricks should mean that mentions of this -- class "cancel out" mentions of CouldBeF. As an example, let's -- imagine a function that represents some business logic that -- potentially "throws" either an Int or Bool while it -- runs: -- --
--   >>> :set -XFlexibleContexts -XMonoLocalBinds -XTypeOperators
--   
--   >>> :{
--   f :: (e `CouldBe` Int, e `CouldBe` Bool) => VariantF IO e
--   f = throwF (pure True)
--   :}
--   
-- -- As we can see, there are two constraints here. However, if we "catch" -- one of these possible errors, we don't just add the CatchF -- constraint: we /cancel out/ the constraint corresponding to the type -- we caught: -- --
--   >>> :{
--   g :: e `CouldBe` Int => Either (VariantF IO e) (IO Bool)
--   g = catchF @Bool f
--   :}
--   
-- -- This means that constraints only propagate for uncaught -- exceptions, just as Java functions only need declare exceptions they -- haven't caught. Once we've caught all the errors, the -- constraint disappears! This can be a nice way to work if you combine -- it with something like ExceptT. class CatchF x xs ys | xs x -> ys, xs ys -> x, x ys -> xs catchF :: CatchF x xs ys => VariantF f xs -> Either (VariantF f ys) (f x) -- | throwF is to catchF as throw is to -- catch. This function allows us to discharge constraints for -- Variant types. We can revisit the catchF example without -- the functor wrapper: -- --
--   >>> :{
--   f :: (e `CouldBe` Int, e `CouldBe` Bool) => Variant e
--   f = throw True
--   :}
--   
-- -- ... and be similarly excited when we make one of the constraints -- disappear: -- --
--   >>> :{
--   g :: e `CouldBe` Int => Either (Variant e) Bool
--   g = catch @Bool f
--   :}
--   
class CatchF x xs ys => Catch (x :: Type) (xs :: [Type]) (ys :: [Type]) catch :: Catch x xs ys => Variant xs -> Either (Variant ys) x -- | Occasionally, we might want to use our "nested Either" analogue -- for whatever reason. For that situation the functions here allow you -- to swap between the two representations. -- --
--   > > :t toEithersF @IO @'[String, Int, Bool]
--   
-- -- toEithersF IO '[String, Int, Bool] :: VariantF IO '[String, -- Int, Bool] -> Either (IO [Char]) (Either (IO Int) (IO Bool)) -- -- In order to maintain the round-tripping property (see below), the -- functional dependency only goes from the variant to the nested either. -- This is because the opposite doesn't always necessarily make sense. -- -- If Variant '[a, b] is converted to Either a b, it -- would seem sensible to say the opposite is equally as mechanical. -- However, consider a nesting like Either a (Either b c): -- should this translate to Variant '[a, b, c] or Variant -- '[a, Either b c]? There's not a unique mapping in this direction, -- so we can't add the functional dependency. class EithersF (f :: Type -> Type) (xs :: [Type]) (o :: Type) | f xs -> o, o f -> xs toEithersF :: EithersF f xs o => VariantF f xs -> o fromEithersF :: EithersF f xs o => o -> VariantF f xs -- | The f-less analogue of EithersF. The same properties -- as described above will hold, with the same issues around -- fromEithers result inference. -- --
--   > > :t toEithers @'[String, Int, Bool]
--   
-- -- toEithers @'[String, Int, Bool] :: Variant '[String, Int, Bool] -> -- Either [Char] (Either Int Bool) -- -- The round-tripping property is also conserved: class Eithers (xs :: [Type]) (o :: Type) | xs -> o toEithers :: Eithers xs o => Variant xs -> o fromEithers :: Eithers xs o => o -> Variant xs -- | A constraint-based fold requires a polymorphic function relying on a -- shared constraint between all members of the variant. If that's a lot -- of words, let's see a little example: -- --
--   >>> foldF @Show (throwF ["hello"] :: VariantF [] '[(), String, Bool]) show
--   "[\"hello\"]"
--   
-- -- If everything in our variant is Show-friendly, we can fold it -- with the show function, and we just show whatever is in there! class FoldF (c :: Type -> Constraint) (xs :: [Type]) foldF :: FoldF c xs => VariantF f xs -> (forall x. c x => f x -> m) -> m -- | Similarly, we can fold the wrapper-less version in the same way. As an -- example, if all the types are the same, we can pull out whatever value -- is in there using the fold interface. -- --
--   >>> :set -XRankNTypes -XScopedTypeVariables
--   
--   >>> :{
--   fold' :: forall x xs. Fold ((~) x) xs => Variant xs -> x
--   fold' xs = fold @((~) x) xs id
--   :}
--   
-- -- If all the types in the list are the same, and we can turn values of -- that type into some result and return it. class FoldF c xs => Fold (c :: Type -> Constraint) (xs :: [Type]) fold :: Fold c xs => Variant xs -> (forall x. c x => x -> m) -> m -- | A choice of zero types is an uninhabited type! This means we can -- convert it to Void... preposterous :: VariantF f '[] -> Void -- | ... and it also means we can convert back! postposterous :: Void -> VariantF f '[] instance forall k (f :: k -> *) (xs :: [k]). Data.Variant.AllF GHC.Classes.Eq f xs => GHC.Classes.Eq (Data.Variant.VariantF f xs) instance forall k (f :: k -> *) (xs :: [k]). Data.Variant.AllF GHC.Show.Show f xs => GHC.Show.Show (Data.Variant.VariantF f xs) instance forall k (f :: k -> *) (xs :: [k]). (Data.Variant.AllF GHC.Classes.Eq f xs, Data.Variant.AllF GHC.Classes.Ord f xs) => GHC.Classes.Ord (Data.Variant.VariantF f xs) instance Data.Variant.FoldF c xs => Data.Variant.Fold c xs instance Data.Variant.FoldF c '[] instance (c x, Data.Variant.FoldF c xs) => Data.Variant.FoldF c (x : xs) instance Data.Variant.Eithers '[x] x instance Data.Variant.Eithers (y : xs) zs => Data.Variant.Eithers (x : y : xs) (Data.Either.Either x zs) instance Data.Variant.EithersF f '[x] (f x) instance (GHC.Base.Functor f, Data.Variant.EithersF f (y : xs) zs) => Data.Variant.EithersF f (x : y : xs) (Data.Either.Either (f x) zs) instance (Data.Variant.EithersF f xs nested, Test.QuickCheck.Arbitrary.Arbitrary nested) => Test.QuickCheck.Arbitrary.Arbitrary (Data.Variant.VariantF f xs) instance Data.Variant.CatchF x xs ys => Data.Variant.Catch x xs ys instance forall a (x :: a) (xs :: [a]). Data.Variant.CatchF x (x : xs) xs instance forall a (y :: a) (z :: a) (x :: a) (xs :: [a]) (ys :: [a]). (y GHC.Types.~ z, Data.Variant.CatchF x xs ys) => Data.Variant.CatchF x (y : xs) (z : ys) instance Data.Variant.CouldBeF xs x => Data.Variant.CouldBe xs x instance forall k (x :: k) (xs :: [k]). Data.Variant.CouldBeF (x : xs) x instance forall k (xs :: [k]) (x :: k) (y :: k). Data.Variant.CouldBeF xs x => Data.Variant.CouldBeF (y : xs) x instance forall k (x :: k). Data.Variant.TypeNotFound x => Data.Variant.CouldBeF '[] x instance Data.Variant.Case '[x] r ((x -> r) -> r) instance Data.Variant.Case (y : zs) r ((y -> r) -> o) => Data.Variant.Case (x : y : zs) r ((x -> r) -> (y -> r) -> o) instance Data.Variant.CaseF '[x] f r ((f x -> r) -> r) instance Data.Variant.CaseF (y : zs) f r ((f y -> r) -> o) => Data.Variant.CaseF (x : y : zs) f r ((f x -> r) -> (f y -> r) -> o) instance forall k (f :: k -> *) (xs :: [k]). Data.Variant.AllF GHC.Base.Semigroup f xs => GHC.Base.Semigroup (Data.Variant.VariantF f xs) instance forall a (f :: a -> *) (x :: a) (xs :: [a]). (GHC.Base.Monoid (f x), GHC.Base.Semigroup (Data.Variant.VariantF f (x : xs))) => GHC.Base.Monoid (Data.Variant.VariantF f (x : xs)) module Control.Monad.Oops -- | When working in some monadic context, using catch becomes -- trickier. The intuitive behaviour is that each catch shrinks -- the variant in the left side of my MonadError, but this is -- therefore type-changing: as we can only throwError and -- catchError with a MonadError type, this is impossible! -- -- To get round this problem, we have to specialise to ExceptT, -- which allows us to map over the error type and change it as we go. If -- the error we catch is the one in the variant that we want to handle, -- we pluck it out and deal with it. Otherwise, we "re-throw" the variant -- minus the one we've handled. catchF :: forall x e e' f m a. () => Monad m => CatchF x e e' => (f x -> ExceptT (VariantF f e') m a) -> ExceptT (VariantF f e) m a -> ExceptT (VariantF f e') m a -- | Just the same as catchF, but specialised for our plain -- Variant and sounding much less like a radio station. catch :: forall x e e' m a. () => Monad m => Catch x e e' => (x -> ExceptT (Variant e') m a) -> ExceptT (Variant e) m a -> ExceptT (Variant e') m a -- | Throw an error into a variant MonadError context. Note that -- this isn't type-changing, so this can work for any -- MonadError, rather than just ExceptT. throwF :: forall x e f m a. () => MonadError (VariantF f e) m => e `CouldBe` x => f x -> m a -- | Same as throwF, but without the f context. Given a -- value of some type within a Variant within a MonadError -- context, "throw" the error. throw :: forall x e m a. () => MonadError (Variant e) m => e `CouldBe` x => x -> m a -- | Same as catchF except the error is not removed from the type. -- This is useful for writing recursive computations or computations that -- rethrow the same error type. snatchF :: forall x e f m a. () => Monad m => e `CouldBe` x => (f x -> ExceptT (VariantF f e) m a) -> ExceptT (VariantF f e) m a -> ExceptT (VariantF f e) m a -- | Same as catch except the error is not removed from the type. -- This is useful for writing recursive computations or computations that -- rethrow the same error type. snatch :: forall x e m a. () => Monad m => e `CouldBe` x => (x -> ExceptT (Variant e) m a) -> ExceptT (Variant e) m a -> ExceptT (Variant e) m a -- | When dealing with larger (or polymorphic) variants, it becomes -- difficult (or impossible) to construct VariantF values -- explicitly. In that case, the throwF function gives us a -- polymorphic way to lift values into variants. -- --
--   >>> throwF (pure "Hello") :: VariantF Maybe '[Bool, Int, Double, String]
--   There (There (There (Here (Just "Hello"))))
--   
-- --
--   >>> throwF (pure True) :: VariantF Maybe '[Bool, Int, Double, String]
--   Here (Just True)
--   
-- --
--   >>> throwF (pure True) :: VariantF IO '[Int, Double, String]
--   ...
--   ... • Uh oh! I couldn't find Bool inside the variant!
--   ...   If you're pretty sure I'm wrong, perhaps the variant type is ambiguous;
--   ...   could you add some annotations?
--   ...
--   
class CouldBeF (xs :: [k]) (x :: k) -- | Just as with CouldBeF, we can "throw" values not in a -- functor context into a regular Variant. -- --
--   >>> throw (3 :: Int) :: Variant '[Bool, Int, Double, String]
--   There (Here (Identity 3))
--   
-- --
--   >>> throw "Woo!" :: Variant '[Bool, Int, Double, String]
--   There (There (There (Here (Identity "Woo!"))))
--   
class CouldBeF xs x => CouldBe (xs :: [Type]) (x :: Type) -- | As with CouldBeAnyOf, we can also constrain a variant to -- represent several possible types, as we might with several -- CouldBeF constraints, using one type-level list. type e `CouldBeAnyOfF` xs = All (Map (CouldBeF e) xs) -- | Listing larger variants' constraints might amplify the noise of -- functions' signatures. The CouldBeAnyOfF constraint lets us -- specify several types a variant may contain in a single type-level -- list, as opposed to several independent constraints. So, we could -- replace, -- -- f :: (e CouldBe Int, e CouldBe Bool, e CouldBe -- Char) => VariantF IO e -- -- with the equivalent constraint, -- -- f :: e CouldBeAnyOf '[Int, Bool, Char] => VariantF IO e -- -- As CouldBeAnyOf is just short-hand, we can use throw -- just like when we have CouldBe constraints: -- --
--   >>> :set -XTypeOperators
--   
--   >>> :{
--   f :: e `CouldBeAnyOf` '[Int, Bool, Char] => Variant e
--   f = throw 'c'
--   :}
--   
-- -- ... and eliminate constraints in just the same way: -- --
--   >>> :{
--   g :: e `CouldBeAnyOf` '[Int, Bool] => Either (Variant e) Char
--   g = catch @Char f
--   :}
--   
type e `CouldBeAnyOf` xs = All (Map (CouldBe e) xs) -- | Often, you'll want to have a choice of types that aren't all -- wrapped in a functor. For this, we provide the Variant type -- synonym, as well as equivalents of all the functions below. These -- functions take care of wrapping and unwrapping the Identity -- wrapper, too, so it should be invisible to users. type Variant (xs :: [Type]) = VariantF Identity xs -- | The type VariantF f '[x, y, z] is either f x, -- f y, or f z. The We construct these with -- Here, There . Here, and There . There . -- Here respectively, and we can think o fthe number of -- There-nestings as being the index of our chosen type in the -- type-level list of options. -- -- Often, however, we'll want to avoid being too explicit about our list -- of types, preferring instead to describe it with constraints. See the -- methods below for more information! -- --
--   > > :t [ Here (pure "Hello"), There (Here (pure True)) ]
--   
-- -- data VariantF (f :: k -> Type) (xs :: [k]) -- | Add 'ExceptT (Variant '[])' to the monad transformer stack. runOops :: () => Monad m => ExceptT (Variant '[]) m a -> m a -- | Run an oops expression that throws one error in an ExceptT. runOopsInExceptT :: forall x m a. Monad m => ExceptT (Variant '[x]) m a -> ExceptT x m a -- | Run an oops expression that throws one error in an Either. -- -- This function can also be implemented this way (which could be -- instructive for implementing your own combinators) runOopsInEither :: forall x m a. Monad m => ExceptT (Variant '[x]) m a -> m (Either x a) -- | Suspend the ExceptT monad transformer from the top of the stack -- so that the stack can be manipulated without the ExceptT layer. suspend :: forall x m a n b. () => (m (Either x a) -> n (Either x b)) -> ExceptT x m a -> ExceptT x n b -- | Catch the specified exception and return the caught value as -- Left. If no value was caught, then return the returned value in -- Right. catchOrMap :: forall x a e' m b. Monad m => (b -> a) -> (x -> ExceptT (Variant e') m a) -> ExceptT (Variant (x : e')) m b -> ExceptT (Variant e') m a -- | Catch the specified exception and return the caught value as -- Left. If no value was caught, then return the returned value in -- Right. catchAsLeft :: forall x e m a. () => Monad m => ExceptT (Variant (x : e)) m a -> ExceptT (Variant e) m (Either x a) -- | Catch the specified exception and return Nothing. If no value -- was caught, then return the returned value in Just. catchAsNothing :: forall x e m a. () => Monad m => ExceptT (Variant (x : e)) m a -> ExceptT (Variant e) m (Maybe a) -- | Catch the specified exception. If that exception is caught, exit the -- program. catchAndExitFailure :: forall x e m a. () => MonadIO m => ExceptT (Variant (x : e)) m a -> ExceptT (Variant e) m a -- | Catch the specified exception and return it instead. The evaluated -- computation must return the same type that is being caught. recover :: forall x e m a. () => Monad m => (x -> a) -> ExceptT (Variant (x : e)) m a -> ExceptT (Variant e) m a -- | Catch the specified exception and return it instead. The evaluated -- computation must return Void (ie. it never returns) recoverOrVoid :: forall x e m. () => Monad m => ExceptT (Variant (x : e)) m Void -> ExceptT (Variant e) m x -- | Handle the Left constructor of the returned Either onLeft :: forall x m a. () => Monad m => (x -> m a) -> m (Either x a) -> m a -- | Handle the Nothing constructor of the returned Maybe onNothing :: forall m a. () => Monad m => m a -> m (Maybe a) -> m a -- | When the expression of type 'm (Either x a)' evaluates to 'pure (Left -- x)', throw the x, otherwise return a. onLeftThrow :: forall x e m a. () => MonadError (Variant e) m => e `CouldBe` x => m (Either x a) -> m a -- | When the expression of type 'Maybe a' evaluates to Nothing, -- throw the specified value, otherwise return a. onNothingThrow :: forall e es m a. () => MonadError (Variant es) m => CouldBe es e => e -> m (Maybe a) -> m a -- | When the expression of type 'Either x a' evaluates to 'Left x', throw -- the x, otherwise return a. hoistEither :: forall x e m a. () => MonadError (Variant e) m => e `CouldBe` x => Monad m => Either x a -> m a -- | When the expression of type 'Maybe a' evaluates to Nothing, -- throw the specified value, otherwise return a. hoistMaybe :: forall e es m a. () => MonadError (Variant es) m => CouldBe es e => e -> Maybe a -> m a -- | Catch an exception of the specified type x and throw it as an -- error onExceptionThrow :: forall x e m a. () => MonadCatch m => Exception x => MonadError (Variant e) m => e `CouldBe` x => m a -> m a -- | Catch an exception of the specified type x and call the the -- handler h onException :: forall x m a. () => MonadCatch m => Exception x => (x -> m a) -> m a -> m a