{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE QuantifiedConstraints #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeInType #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UnboxedTuples #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -Wno-simplifiable-class-constraints #-}
module Capability.Error
(
HasThrow(..)
, throw
, HasCatch(..)
, catch
, catchJust
, wrapError
, HasThrow'
, HasCatch'
, TypeOf
, MonadError(..)
, MonadThrow(..)
, MonadCatch(..)
, SafeExceptions(..)
, MonadUnliftIO(..)
, module Capability.Accessors
, Exception(..)
, Typeable
) where
import Capability.Accessors
import Capability.Constraints
import Capability.Derive (derive)
import Capability.TypeOf
import Control.Exception (Exception(..))
import qualified Control.Exception.Safe as Safe
import Control.Lens (preview, review)
import Control.Monad ((<=<))
import qualified Control.Monad.Catch as Catch
import qualified Control.Monad.Except as Except
import Control.Monad.IO.Class (MonadIO)
import qualified Control.Monad.IO.Unlift as UnliftIO
import Control.Monad.Primitive (PrimMonad)
import Control.Monad.Trans.Class (MonadTrans, lift)
import Control.Monad.Trans.Control (MonadTransControl(..))
import Data.Coerce (Coercible, coerce)
import qualified Data.Generics.Sum.Constructors as Generic
import Data.Typeable (Typeable)
import GHC.Exts (Proxy#, proxy#)
import qualified UnliftIO.Exception as UnliftIO
class Monad m
=> HasThrow (tag :: k) (e :: *) (m :: * -> *) | tag m -> e
where
throw_ :: Proxy# tag -> e -> m a
throw :: forall tag e m a. HasThrow tag e m => e -> m a
throw = throw_ (proxy# @_ @tag)
{-# INLINE throw #-}
class HasThrow tag e m
=> HasCatch (tag :: k) (e :: *) (m :: * -> *) | tag m -> e
where
catch_ :: Proxy# tag -> m a -> (e -> m a) -> m a
catchJust_ :: Proxy# tag -> (e -> Maybe b) -> m a -> (b -> m a) -> m a
catch :: forall tag e m a. HasCatch tag e m => m a -> (e -> m a) -> m a
catch = catch_ (proxy# @_ @tag)
{-# INLINE catch #-}
catchJust :: forall tag e m a b. HasCatch tag e m
=> (e -> Maybe b) -> m a -> (b -> m a) -> m a
catchJust = catchJust_ (proxy# @_ @tag)
{-# INLINE catchJust #-}
wrapError :: forall innertag t (cs :: [Capability]) inner m a.
( forall x. Coercible (t m x) (m x)
, HasCatch innertag inner (t m)
, All cs m)
=> (forall m'. All (HasCatch innertag inner ': cs) m' => m' a) -> m a
wrapError =
derive @t @'[HasCatch innertag inner] @cs
{-# INLINE wrapError #-}
newtype MonadError m (a :: *) = MonadError (m a)
deriving (Functor, Applicative, Monad, MonadIO, PrimMonad)
instance Except.MonadError e m => HasThrow tag e (MonadError m) where
throw_ :: forall a. Proxy# tag -> e -> MonadError m a
throw_ _ = coerce @(e -> m a) $ Except.throwError
{-# INLINE throw_ #-}
instance Except.MonadError e m => HasCatch tag e (MonadError m) where
catch_ :: forall a.
Proxy# tag -> MonadError m a -> (e -> MonadError m a) -> MonadError m a
catch_ _ = coerce @(m a -> (e -> m a) -> m a) $ Except.catchError
{-# INLINE catch_ #-}
catchJust_ :: forall a b.
Proxy# tag
-> (e -> Maybe b)
-> MonadError m a
-> (b -> MonadError m a)
-> MonadError m a
catchJust_ tag f m h = catch_ tag m $ \e -> maybe (throw_ tag e) h $ f e
{-# INLINE catchJust_ #-}
newtype MonadThrow (e :: *) m (a :: *) = MonadThrow (m a)
deriving (Functor, Applicative, Monad, MonadIO, PrimMonad)
instance (Catch.Exception e, Catch.MonadThrow m)
=> HasThrow tag e (MonadThrow e m)
where
throw_ :: forall a. Proxy# tag -> e -> MonadThrow e m a
throw_ _ = coerce @(e -> m a) $ Catch.throwM
{-# INLINE throw_ #-}
newtype MonadCatch (e :: *) m (a :: *) = MonadCatch (m a)
deriving (Functor, Applicative, Monad, MonadIO, PrimMonad)
deriving (HasThrow tag e) via MonadThrow e m
instance (Catch.Exception e, Catch.MonadCatch m)
=> HasCatch tag e (MonadCatch e m)
where
catch_ :: forall a.
Proxy# tag
-> MonadCatch e m a
-> (e -> MonadCatch e m a)
-> MonadCatch e m a
catch_ _ = coerce @(m a -> (e -> m a) -> m a) $ Catch.catch
{-# INLINE catch_ #-}
catchJust_ :: forall a b.
Proxy# tag
-> (e -> Maybe b)
-> MonadCatch e m a
-> (b -> MonadCatch e m a)
-> MonadCatch e m a
catchJust_ _ = coerce @((e -> Maybe b) -> m a -> (b -> m a) -> m a) $
Catch.catchJust
{-# INLINE catchJust_ #-}
newtype SafeExceptions (e :: *) m (a :: *) = SafeExceptions (m a)
deriving (Functor, Applicative, Monad, MonadIO, PrimMonad)
instance (Safe.Exception e, Safe.MonadThrow m)
=> HasThrow tag e (SafeExceptions e m)
where
throw_ :: forall a. Proxy# tag -> e -> SafeExceptions e m a
throw_ _ = coerce @(e -> m a) $ Safe.throw
{-# INLINE throw_ #-}
instance (Safe.Exception e, Safe.MonadCatch m)
=> HasCatch tag e (SafeExceptions e m)
where
catch_ :: forall a.
Proxy# tag
-> SafeExceptions e m a
-> (e -> SafeExceptions e m a)
-> SafeExceptions e m a
catch_ _ = coerce @(m a -> (e -> m a) -> m a) $ Safe.catch
{-# INLINE catch_ #-}
catchJust_ :: forall a b.
Proxy# tag
-> (e -> Maybe b)
-> SafeExceptions e m a
-> (b -> SafeExceptions e m a)
-> SafeExceptions e m a
catchJust_ _ = coerce @((e -> Maybe b) -> m a -> (b -> m a) -> m a) $
Safe.catchJust
{-# INLINE catchJust_ #-}
newtype MonadUnliftIO (e :: *) m (a :: *) = MonadUnliftIO (m a)
deriving (Functor, Applicative, Monad, MonadIO, PrimMonad)
instance (UnliftIO.Exception e, MonadIO m)
=> HasThrow tag e (MonadUnliftIO e m)
where
throw_ :: forall a. Proxy# tag -> e -> MonadUnliftIO e m a
throw_ _ = coerce @(e -> m a) $ UnliftIO.throwIO
{-# INLINE throw_ #-}
instance (UnliftIO.Exception e, UnliftIO.MonadUnliftIO m)
=> HasCatch tag e (MonadUnliftIO e m)
where
catch_ :: forall a.
Proxy# tag
-> MonadUnliftIO e m a
-> (e -> MonadUnliftIO e m a)
-> MonadUnliftIO e m a
catch_ _ = coerce @(m a -> (e -> m a) -> m a) $ UnliftIO.catch
{-# INLINE catch_ #-}
catchJust_ :: forall a b.
Proxy# tag
-> (e -> Maybe b)
-> MonadUnliftIO e m a
-> (b -> MonadUnliftIO e m a)
-> MonadUnliftIO e m a
catchJust_ _ = coerce @((e -> Maybe b) -> m a -> (b -> m a) -> m a) $
UnliftIO.catchJust
{-# INLINE catchJust_ #-}
instance HasThrow oldtag e m => HasThrow newtag e (Rename oldtag m) where
throw_ :: forall a. Proxy# newtag -> e -> Rename oldtag m a
throw_ _ = coerce @(e -> m a) $ throw @oldtag
{-# INLINE throw_ #-}
instance HasCatch oldtag e m => HasCatch newtag e (Rename oldtag m) where
catch_ :: forall a.
Proxy# newtag
-> Rename oldtag m a
-> (e -> Rename oldtag m a)
-> Rename oldtag m a
catch_ _ = coerce @(m a -> (e -> m a) -> m a) $ catch @oldtag
{-# INLINE catch_ #-}
catchJust_ :: forall a b.
Proxy# newtag
-> (e -> Maybe b)
-> Rename oldtag m a
-> (b -> Rename oldtag m a)
-> Rename oldtag m a
catchJust_ _ = coerce @((e -> Maybe b) -> m a -> (b -> m a) -> m a) $
catchJust @oldtag
{-# INLINE catchJust_ #-}
instance
(Generic.AsConstructor' ctor sum e, HasThrow oldtag sum m)
=> HasThrow ctor e (Ctor ctor oldtag m)
where
throw_ :: forall a. Proxy# ctor -> e -> Ctor ctor oldtag m a
throw_ _ = coerce @(e -> m a) $
throw @oldtag . review (Generic._Ctor' @ctor @sum)
{-# INLINE throw_ #-}
instance
(Generic.AsConstructor' ctor sum e, HasCatch oldtag sum m)
=> HasCatch ctor e (Ctor ctor oldtag m)
where
catch_ :: forall a.
Proxy# ctor
-> Ctor ctor oldtag m a
-> (e -> Ctor ctor oldtag m a)
-> Ctor ctor oldtag m a
catch_ _ = coerce @(m a -> (e -> m a) -> m a) $
catchJust @oldtag @sum $ preview (Generic._Ctor' @ctor @sum)
{-# INLINE catch_ #-}
catchJust_ :: forall a b.
Proxy# ctor
-> (e -> Maybe b)
-> Ctor ctor oldtag m a
-> (b -> Ctor ctor oldtag m a)
-> Ctor ctor oldtag m a
catchJust_ _ = coerce @((e -> Maybe b) -> m a -> (b -> m a) -> m a) $ \f ->
catchJust @oldtag @sum $ f <=< preview (Generic._Ctor' @ctor @sum)
{-# INLINE catchJust_ #-}
instance
( HasThrow tag e m, MonadTrans t, Monad (t m) )
=> HasThrow tag e (Lift (t m))
where
throw_ :: forall a. Proxy# tag -> e -> Lift (t m) a
throw_ tag = coerce @(e -> t m a) $ lift . throw_ tag
{-# INLINE throw_ #-}
instance
( HasCatch tag e m, MonadTransControl t, Monad (t m) )
=> HasCatch tag e (Lift (t m))
where
catch_ :: forall a.
Proxy# tag
-> Lift (t m) a
-> (e -> Lift (t m) a)
-> Lift (t m) a
catch_ tag = coerce @(t m a -> (e -> t m a) -> t m a) $ \m h ->
liftWith (\run -> catch_ tag (run m) (run . h)) >>= restoreT . pure
{-# INLINE catch_ #-}
catchJust_ :: forall a b.
Proxy# tag
-> (e -> Maybe b)
-> Lift (t m) a
-> (b -> Lift (t m) a)
-> Lift (t m) a
catchJust_ tag =
coerce @((e -> Maybe b) -> t m a -> (b -> t m a) -> t m a) $ \f m h ->
liftWith (\run -> catchJust_ tag f (run m) (run . h)) >>= restoreT . pure
{-# INLINE catchJust_ #-}
deriving via ((t2 :: (* -> *) -> * -> *) ((t1 :: (* -> *) -> * -> *) m))
instance
( forall x. Coercible (m x) (t2 (t1 m) x)
, Monad m, HasThrow tag e (t2 (t1 m)) )
=> HasThrow tag e ((t2 :.: t1) m)
deriving via ((t2 :: (* -> *) -> * -> *) ((t1 :: (* -> *) -> * -> *) m))
instance
( forall x. Coercible (m x) (t2 (t1 m) x)
, Monad m, HasCatch tag e (t2 (t1 m)) )
=> HasCatch tag e ((t2 :.: t1) m)
type HasThrow' (tag :: k) = HasThrow tag (TypeOf k tag)
type HasCatch' (tag :: k) = HasCatch tag (TypeOf k tag)