{-# LANGUAGE CPP #-}
-- | Support for runtime exceptions.
--
-- This module supplies thin wrappers over functions from "Control.Exception" as
-- well as several utility functions for convenience.
--
-- /Note:/ the 'Eff' monad provides instances for 'C.MonadThrow', 'C.MonadCatch'
-- and 'C.MonadMask', so any existing code that uses them remains compatible.
module Effectful.Exception
  ( -- * Throwing
    throwIO
#if MIN_VERSION_base(4,21,0)
  , rethrowIO
#endif

    -- * Catching (with recovery)
    -- $catchAll
  , catch
#if MIN_VERSION_base(4,21,0)
  , catchNoPropagate
#endif
  , catchDeep
  , catchJust
  , catchIf
  , catchIO
  , catchSync
  , catchSyncDeep

  , handle
  , handleDeep
  , handleJust
  , handleIf
  , handleIO
  , handleSync
  , handleSyncDeep

  , try
#if MIN_VERSION_base(4,21,0)
  , tryWithContext
#endif
  , tryDeep
  , tryJust
  , tryIf
  , tryIO
  , trySync
  , trySyncDeep

  , C.Handler(..)
  , catches
  , catchesDeep

    -- | #cleanup#

    -- * Cleanup (no recovery)
  , bracket
  , bracket_
  , bracketOnError
  , generalBracket
  , C.ExitCase(..)
  , finally
  , onException

    -- * Utils

    -- ** Evaluation
  , evaluate
  , evaluateDeep

#if MIN_VERSION_base(4,20,0)
    -- ** Annotations
  , annotateIO
#endif

    -- | #checkExceptionType#

    -- ** Check exception type
    -- $syncVsAsync
  , isSyncException
  , isAsyncException

    -- * Low-level API
  , mask
  , mask_
  , uninterruptibleMask
  , uninterruptibleMask_
  , E.MaskingState(..)
  , getMaskingState
  , interruptible
  , allowInterrupt

    -- * Re-exports from "Control.Exception"

    -- ** The 'SomeException' type
  , E.SomeException(..)

    -- ** The 'Exception' class
  , E.Exception(..)
  , E.mapException

#if MIN_VERSION_base(4,20,0)
    -- ** Exception context and annotation
  , E.addExceptionContext
  , E.someExceptionContext
  , E.ExceptionWithContext(..)
#if MIN_VERSION_base(4,21,0)
  , E.WhileHandling(..)
#endif
  , E.ExceptionContext(..)
  , E.emptyExceptionContext
  , E.addExceptionAnnotation
  , E.getExceptionAnnotations
  , E.getAllExceptionAnnotations
  , E.displayExceptionContext
  , E.SomeExceptionAnnotation(..)
  , E.ExceptionAnnotation(..)
#endif

    -- ** Concrete exception types
  , E.IOException
  , E.ArithException(..)
  , E.ArrayException(..)
  , E.AssertionFailed(..)
  , E.NoMethodError(..)
  , E.PatternMatchFail(..)
  , E.RecConError(..)
  , E.RecSelError(..)
  , E.RecUpdError(..)
  , E.ErrorCall(..)
  , E.TypeError(..)

    -- ** Asynchronous exceptions
  , E.SomeAsyncException(..)
  , E.AsyncException(..)
  , E.asyncExceptionToException
  , E.asyncExceptionFromException
  , E.NonTermination(..)
  , E.NestedAtomically(..)
  , E.BlockedIndefinitelyOnMVar(..)
  , E.BlockedIndefinitelyOnSTM(..)
  , E.AllocationLimitExceeded(..)
  , E.CompactionFailed(..)
  , E.Deadlock(..)

    -- ** Assertions
  , E.assert
  ) where

#if MIN_VERSION_base(4,20,0)
import Control.Exception.Annotation qualified as E
import Control.Exception.Context qualified as E
#endif

import Control.DeepSeq
import Control.Exception qualified as E
import Control.Monad.Catch qualified as C
import GHC.Stack (withFrozenCallStack)

import Effectful
import Effectful.Dispatch.Static
import Effectful.Dispatch.Static.Unsafe

----------------------------------------
-- Throwing

-- | Lifted 'E.throwIO'.
throwIO
  :: (HasCallStack, E.Exception e)
  => e
  -- ^ The error.
  -> Eff es a
throwIO :: forall e (es :: [Effect]) a.
(HasCallStack, Exception e) =>
e -> Eff es a
throwIO = IO a -> Eff es a
forall a (es :: [Effect]). IO a -> Eff es a
unsafeEff_ (IO a -> Eff es a) -> (e -> IO a) -> e -> Eff es a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (HasCallStack => e -> IO a) -> e -> IO a
forall a. HasCallStack => (HasCallStack => a) -> a
withFrozenCallStack e -> IO a
HasCallStack => e -> IO a
forall e a. Exception e => e -> IO a
E.throwIO

#if MIN_VERSION_base(4,21,0)
-- | Lifted 'E.rethrowIO'.
rethrowIO
  :: E.Exception e
  => E.ExceptionWithContext e
  -> Eff es a
rethrowIO = unsafeEff_ . E.rethrowIO
#endif

----------------------------------------
-- Catching

-- $catchAll
--
-- /Note:/ __do not use 'catch', 'handle' or 'try' to catch 'E.SomeException'__
-- unless you're really sure you want to catch __all__ exceptions (including
-- asynchronous ones). Instead:
--
-- - If you want to catch all exceptions, run a cleanup action and rethrow, use
--   one of the functions from the [cleanup](#cleanup) section.
--
-- - If you want to catch all synchronous exceptions, use 'catchSync',
--   'handleSync' or 'trySync'.

-- | Lifted 'E.catch'.
catch
  :: E.Exception e
  => Eff es a
  -> (e -> Eff es a)
  -- ^ The exception handler.
  -> Eff es a
catch :: forall e (es :: [Effect]) a.
Exception e =>
Eff es a -> (e -> Eff es a) -> Eff es a
catch Eff es a
action e -> Eff es a
handler = ((forall r. Eff es r -> IO r) -> IO a) -> Eff es a
forall (es :: [Effect]) a.
((forall r. Eff es r -> IO r) -> IO a) -> Eff es a
reallyUnsafeUnliftIO (((forall r. Eff es r -> IO r) -> IO a) -> Eff es a)
-> ((forall r. Eff es r -> IO r) -> IO a) -> Eff es a
forall a b. (a -> b) -> a -> b
$ \forall r. Eff es r -> IO r
unlift -> do
  IO a -> (e -> IO a) -> IO a
forall e a. Exception e => IO a -> (e -> IO a) -> IO a
E.catch (Eff es a -> IO a
forall r. Eff es r -> IO r
unlift Eff es a
action) (Eff es a -> IO a
forall r. Eff es r -> IO r
unlift (Eff es a -> IO a) -> (e -> Eff es a) -> e -> IO a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. e -> Eff es a
handler)

-- | A variant of 'catch' that fully forces evaluation of the result value to
-- find all impure exceptions.
catchDeep
  :: (E.Exception e, NFData a)
  => Eff es a
  -> (e -> Eff es a)
  -- ^ The exception handler.
  -> Eff es a
catchDeep :: forall e a (es :: [Effect]).
(Exception e, NFData a) =>
Eff es a -> (e -> Eff es a) -> Eff es a
catchDeep Eff es a
action = Eff es a -> (e -> Eff es a) -> Eff es a
forall e (es :: [Effect]) a.
Exception e =>
Eff es a -> (e -> Eff es a) -> Eff es a
catch (a -> Eff es a
forall a (es :: [Effect]). NFData a => a -> Eff es a
evaluateDeep (a -> Eff es a) -> Eff es a -> Eff es a
forall (m :: Type -> Type) a b. Monad m => (a -> m b) -> m a -> m b
=<< Eff es a
action)

#if MIN_VERSION_base(4,21,0)
-- | Lifted 'E.catchNoPropagate'.
catchNoPropagate
  :: E.Exception e
  => Eff es a
  -> (E.ExceptionWithContext e -> Eff es a)
  -- ^ The exception handler.
  -> Eff es a
catchNoPropagate action handler = reallyUnsafeUnliftIO $ \unlift -> do
  E.catchNoPropagate (unlift action) (unlift . handler)
#endif

-- | Lifted 'E.catchJust'.
catchJust
  :: E.Exception e
  => (e -> Maybe b)
  -- ^ The predicate.
  -> Eff es a
  -> (b -> Eff es a)
  -- ^ The exception handler.
  -> Eff es a
catchJust :: forall e b (es :: [Effect]) a.
Exception e =>
(e -> Maybe b) -> Eff es a -> (b -> Eff es a) -> Eff es a
catchJust e -> Maybe b
f Eff es a
action b -> Eff es a
handler = ((forall r. Eff es r -> IO r) -> IO a) -> Eff es a
forall (es :: [Effect]) a.
((forall r. Eff es r -> IO r) -> IO a) -> Eff es a
reallyUnsafeUnliftIO (((forall r. Eff es r -> IO r) -> IO a) -> Eff es a)
-> ((forall r. Eff es r -> IO r) -> IO a) -> Eff es a
forall a b. (a -> b) -> a -> b
$ \forall r. Eff es r -> IO r
unlift -> do
  (e -> Maybe b) -> IO a -> (b -> IO a) -> IO a
forall e b a.
Exception e =>
(e -> Maybe b) -> IO a -> (b -> IO a) -> IO a
E.catchJust e -> Maybe b
f (Eff es a -> IO a
forall r. Eff es r -> IO r
unlift Eff es a
action) (Eff es a -> IO a
forall r. Eff es r -> IO r
unlift (Eff es a -> IO a) -> (b -> Eff es a) -> b -> IO a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. b -> Eff es a
handler)

-- | Catch an exception only if it satisfies a specific predicate.
catchIf
  :: E.Exception e
  => (e -> Bool)
  -- ^ The predicate.
  -> Eff es a
  -> (e -> Eff es a)
  -- ^ The exception handler.
  -> Eff es a
catchIf :: forall e (es :: [Effect]) a.
Exception e =>
(e -> Bool) -> Eff es a -> (e -> Eff es a) -> Eff es a
catchIf e -> Bool
p = (e -> Maybe e) -> Eff es a -> (e -> Eff es a) -> Eff es a
forall e b (es :: [Effect]) a.
Exception e =>
(e -> Maybe b) -> Eff es a -> (b -> Eff es a) -> Eff es a
catchJust (\e
e -> if e -> Bool
p e
e then e -> Maybe e
forall a. a -> Maybe a
Just e
e else Maybe e
forall a. Maybe a
Nothing)

-- | 'catch' specialized to catch 'IOException'.
catchIO
  :: Eff es a
  -> (E.IOException -> Eff es a)
  -- ^ The exception handler.
  -> Eff es a
catchIO :: forall (es :: [Effect]) a.
Eff es a -> (IOException -> Eff es a) -> Eff es a
catchIO = Eff es a -> (IOException -> Eff es a) -> Eff es a
forall e (es :: [Effect]) a.
Exception e =>
Eff es a -> (e -> Eff es a) -> Eff es a
catch

-- | 'catch' specialized to catch all exceptions considered to be synchronous.
--
-- @'catchSync' ≡ 'catchIf' \@'E.SomeException' 'isSyncException'@
--
-- See the [check exception type](#checkExceptionType) section for more
-- information.
catchSync
  :: Eff es a
  -> (E.SomeException -> Eff es a)
  -- ^ The exception handler.
  -> Eff es a
catchSync :: forall (es :: [Effect]) a.
Eff es a -> (SomeException -> Eff es a) -> Eff es a
catchSync = forall e (es :: [Effect]) a.
Exception e =>
(e -> Bool) -> Eff es a -> (e -> Eff es a) -> Eff es a
catchIf @E.SomeException SomeException -> Bool
forall e. Exception e => e -> Bool
isSyncException

-- | A variant of 'catchSync' that fully forces evaluation of the result value
-- to find all impure exceptions.
catchSyncDeep
  :: NFData a
  => Eff es a
  -> (E.SomeException -> Eff es a)
  -- ^ The exception handler.
  -> Eff es a
catchSyncDeep :: forall a (es :: [Effect]).
NFData a =>
Eff es a -> (SomeException -> Eff es a) -> Eff es a
catchSyncDeep Eff es a
action = Eff es a -> (SomeException -> Eff es a) -> Eff es a
forall (es :: [Effect]) a.
Eff es a -> (SomeException -> Eff es a) -> Eff es a
catchSync (a -> Eff es a
forall a (es :: [Effect]). NFData a => a -> Eff es a
evaluateDeep (a -> Eff es a) -> Eff es a -> Eff es a
forall (m :: Type -> Type) a b. Monad m => (a -> m b) -> m a -> m b
=<< Eff es a
action)

-- | Flipped version of 'catch'.
handle
  :: E.Exception e
  => (e -> Eff es a)
  -- ^ The exception handler.
  -> Eff es a
  -> Eff es a
handle :: forall e (es :: [Effect]) a.
Exception e =>
(e -> Eff es a) -> Eff es a -> Eff es a
handle = (Eff es a -> (e -> Eff es a) -> Eff es a)
-> (e -> Eff es a) -> Eff es a -> Eff es a
forall a b c. (a -> b -> c) -> b -> a -> c
flip Eff es a -> (e -> Eff es a) -> Eff es a
forall e (es :: [Effect]) a.
Exception e =>
Eff es a -> (e -> Eff es a) -> Eff es a
catch

-- | Flipped version of 'catchDeep'.
handleDeep
  :: (E.Exception e, NFData a)
  => (e -> Eff es a)
  -- ^ The exception handler.
  -> Eff es a
  -> Eff es a
handleDeep :: forall e a (es :: [Effect]).
(Exception e, NFData a) =>
(e -> Eff es a) -> Eff es a -> Eff es a
handleDeep = (Eff es a -> (e -> Eff es a) -> Eff es a)
-> (e -> Eff es a) -> Eff es a -> Eff es a
forall a b c. (a -> b -> c) -> b -> a -> c
flip Eff es a -> (e -> Eff es a) -> Eff es a
forall e a (es :: [Effect]).
(Exception e, NFData a) =>
Eff es a -> (e -> Eff es a) -> Eff es a
catchDeep

-- | Flipped version of 'catchJust'.
handleJust
  :: (HasCallStack, E.Exception e)
  => (e -> Maybe b)
  -- ^ The predicate.
  -> (b -> Eff es a)
  -- ^ The exception handler.
  -> Eff es a
  -> Eff es a
handleJust :: forall e b (es :: [Effect]) a.
(HasCallStack, Exception e) =>
(e -> Maybe b) -> (b -> Eff es a) -> Eff es a -> Eff es a
handleJust e -> Maybe b
f = (Eff es a -> (b -> Eff es a) -> Eff es a)
-> (b -> Eff es a) -> Eff es a -> Eff es a
forall a b c. (a -> b -> c) -> b -> a -> c
flip ((e -> Maybe b) -> Eff es a -> (b -> Eff es a) -> Eff es a
forall e b (es :: [Effect]) a.
Exception e =>
(e -> Maybe b) -> Eff es a -> (b -> Eff es a) -> Eff es a
catchJust e -> Maybe b
f)

-- | Flipped version of 'catchIf'.
handleIf
  :: E.Exception e
  => (e -> Bool)
  -- ^ The predicate.
  -> (e -> Eff es a)
  -- ^ The exception handler.
  -> Eff es a
  -> Eff es a
handleIf :: forall e (es :: [Effect]) a.
Exception e =>
(e -> Bool) -> (e -> Eff es a) -> Eff es a -> Eff es a
handleIf e -> Bool
p = (Eff es a -> (e -> Eff es a) -> Eff es a)
-> (e -> Eff es a) -> Eff es a -> Eff es a
forall a b c. (a -> b -> c) -> b -> a -> c
flip ((e -> Bool) -> Eff es a -> (e -> Eff es a) -> Eff es a
forall e (es :: [Effect]) a.
Exception e =>
(e -> Bool) -> Eff es a -> (e -> Eff es a) -> Eff es a
catchIf e -> Bool
p)

-- | Flipped version of 'catchIO'.
handleIO
  :: (E.IOException -> Eff es a)
  -- ^ The exception handler.
  -> Eff es a
  -> Eff es a
handleIO :: forall (es :: [Effect]) a.
(IOException -> Eff es a) -> Eff es a -> Eff es a
handleIO = (Eff es a -> (IOException -> Eff es a) -> Eff es a)
-> (IOException -> Eff es a) -> Eff es a -> Eff es a
forall a b c. (a -> b -> c) -> b -> a -> c
flip Eff es a -> (IOException -> Eff es a) -> Eff es a
forall (es :: [Effect]) a.
Eff es a -> (IOException -> Eff es a) -> Eff es a
catchIO

-- | Flipped version of 'catchSync'.
handleSync
  :: (E.SomeException -> Eff es a)
  -- ^ The exception handler.
  -> Eff es a
  -> Eff es a
handleSync :: forall (es :: [Effect]) a.
(SomeException -> Eff es a) -> Eff es a -> Eff es a
handleSync = (Eff es a -> (SomeException -> Eff es a) -> Eff es a)
-> (SomeException -> Eff es a) -> Eff es a -> Eff es a
forall a b c. (a -> b -> c) -> b -> a -> c
flip Eff es a -> (SomeException -> Eff es a) -> Eff es a
forall (es :: [Effect]) a.
Eff es a -> (SomeException -> Eff es a) -> Eff es a
catchSync

-- | Flipped version of 'catchSyncDeep'.
handleSyncDeep
  :: NFData a
  => (E.SomeException -> Eff es a)
  -- ^ The exception handler.
  -> Eff es a
  -> Eff es a
handleSyncDeep :: forall a (es :: [Effect]).
NFData a =>
(SomeException -> Eff es a) -> Eff es a -> Eff es a
handleSyncDeep = (Eff es a -> (SomeException -> Eff es a) -> Eff es a)
-> (SomeException -> Eff es a) -> Eff es a -> Eff es a
forall a b c. (a -> b -> c) -> b -> a -> c
flip Eff es a -> (SomeException -> Eff es a) -> Eff es a
forall a (es :: [Effect]).
NFData a =>
Eff es a -> (SomeException -> Eff es a) -> Eff es a
catchSyncDeep

-- | Lifted 'E.try'.
try
  :: E.Exception e
  => Eff es a
  -- ^ The action.
  -> Eff es (Either e a)
try :: forall e (es :: [Effect]) a.
Exception e =>
Eff es a -> Eff es (Either e a)
try Eff es a
action = ((forall r. Eff es r -> IO r) -> IO (Either e a))
-> Eff es (Either e a)
forall (es :: [Effect]) a.
((forall r. Eff es r -> IO r) -> IO a) -> Eff es a
reallyUnsafeUnliftIO (((forall r. Eff es r -> IO r) -> IO (Either e a))
 -> Eff es (Either e a))
-> ((forall r. Eff es r -> IO r) -> IO (Either e a))
-> Eff es (Either e a)
forall a b. (a -> b) -> a -> b
$ \forall r. Eff es r -> IO r
unlift -> do
  IO a -> IO (Either e a)
forall e a. Exception e => IO a -> IO (Either e a)
E.try (Eff es a -> IO a
forall r. Eff es r -> IO r
unlift Eff es a
action)

#if MIN_VERSION_base(4,21,0)
-- | Lifted 'E.tryWithContext'.
tryWithContext
  :: E.Exception e
  => Eff es a
  -> Eff es (Either (E.ExceptionWithContext e) a)
tryWithContext action = reallyUnsafeUnliftIO $ \unlift -> do
  E.tryWithContext (unlift action)
#endif

-- | A variant of 'try' that fully forces evaluation of the result value to find
-- all impure exceptions.
tryDeep
  :: (E.Exception e, NFData a)
  => Eff es a
  -- ^ The action.
  -> Eff es (Either e a)
tryDeep :: forall e a (es :: [Effect]).
(Exception e, NFData a) =>
Eff es a -> Eff es (Either e a)
tryDeep Eff es a
action = Eff es a -> Eff es (Either e a)
forall e (es :: [Effect]) a.
Exception e =>
Eff es a -> Eff es (Either e a)
try (a -> Eff es a
forall a (es :: [Effect]). NFData a => a -> Eff es a
evaluateDeep (a -> Eff es a) -> Eff es a -> Eff es a
forall (m :: Type -> Type) a b. Monad m => (a -> m b) -> m a -> m b
=<< Eff es a
action)

-- | Lifted 'E.tryJust'.
tryJust
  :: E.Exception e
  => (e -> Maybe b)
  -- ^ The predicate.
  -> Eff es a
  -> Eff es (Either b a)
tryJust :: forall e b (es :: [Effect]) a.
Exception e =>
(e -> Maybe b) -> Eff es a -> Eff es (Either b a)
tryJust e -> Maybe b
f Eff es a
action = ((forall r. Eff es r -> IO r) -> IO (Either b a))
-> Eff es (Either b a)
forall (es :: [Effect]) a.
((forall r. Eff es r -> IO r) -> IO a) -> Eff es a
reallyUnsafeUnliftIO (((forall r. Eff es r -> IO r) -> IO (Either b a))
 -> Eff es (Either b a))
-> ((forall r. Eff es r -> IO r) -> IO (Either b a))
-> Eff es (Either b a)
forall a b. (a -> b) -> a -> b
$ \forall r. Eff es r -> IO r
unlift -> do
  (e -> Maybe b) -> IO a -> IO (Either b a)
forall e b a.
Exception e =>
(e -> Maybe b) -> IO a -> IO (Either b a)
E.tryJust e -> Maybe b
f (Eff es a -> IO a
forall r. Eff es r -> IO r
unlift Eff es a
action)

-- | Catch an exception only if it satisfies a specific predicate.
tryIf
  :: E.Exception e
  => (e -> Bool)
  -- ^ The predicate.
  -> Eff es a
  -> Eff es (Either e a)
tryIf :: forall e (es :: [Effect]) a.
Exception e =>
(e -> Bool) -> Eff es a -> Eff es (Either e a)
tryIf e -> Bool
p = (e -> Maybe e) -> Eff es a -> Eff es (Either e a)
forall e b (es :: [Effect]) a.
Exception e =>
(e -> Maybe b) -> Eff es a -> Eff es (Either b a)
tryJust (\e
e -> if e -> Bool
p e
e then e -> Maybe e
forall a. a -> Maybe a
Just e
e else Maybe e
forall a. Maybe a
Nothing)

-- | 'try' specialized to catch 'IOException'.
tryIO
  :: Eff es a
  -- ^ The action.
  -> Eff es (Either E.IOException a)
tryIO :: forall (es :: [Effect]) a.
Eff es a -> Eff es (Either IOException a)
tryIO = Eff es a -> Eff es (Either IOException a)
forall e (es :: [Effect]) a.
Exception e =>
Eff es a -> Eff es (Either e a)
try

-- | 'try' specialized to catch all exceptions considered to be synchronous.
--
-- @'trySync' ≡ 'tryIf' \@'E.SomeException' 'isSyncException'@
--
-- See the [check exception type](#checkExceptionType) section for more
-- information.
trySync
  :: Eff es a
  -- ^ The action.
  -> Eff es (Either E.SomeException a)
trySync :: forall (es :: [Effect]) a.
Eff es a -> Eff es (Either SomeException a)
trySync = forall e (es :: [Effect]) a.
Exception e =>
(e -> Bool) -> Eff es a -> Eff es (Either e a)
tryIf @E.SomeException SomeException -> Bool
forall e. Exception e => e -> Bool
isSyncException

-- | A variant of 'trySync' that fully forces evaluation of the result value to
-- find all impure exceptions.
trySyncDeep
  :: NFData a
  => Eff es a
  -- ^ The action.
  -> Eff es (Either E.SomeException a)
trySyncDeep :: forall a (es :: [Effect]).
NFData a =>
Eff es a -> Eff es (Either SomeException a)
trySyncDeep Eff es a
action = Eff es a -> Eff es (Either SomeException a)
forall (es :: [Effect]) a.
Eff es a -> Eff es (Either SomeException a)
trySync (a -> Eff es a
forall a (es :: [Effect]). NFData a => a -> Eff es a
evaluateDeep (a -> Eff es a) -> Eff es a -> Eff es a
forall (m :: Type -> Type) a b. Monad m => (a -> m b) -> m a -> m b
=<< Eff es a
action)

-- | Lifted 'E.catches'.
catches
  :: Eff es a
  -> [C.Handler (Eff es) a]
  -- ^ The exception handlers.
  -> Eff es a
catches :: forall (es :: [Effect]) a.
Eff es a -> [Handler (Eff es) a] -> Eff es a
catches Eff es a
action [Handler (Eff es) a]
handlers = ((forall r. Eff es r -> IO r) -> IO a) -> Eff es a
forall (es :: [Effect]) a.
((forall r. Eff es r -> IO r) -> IO a) -> Eff es a
reallyUnsafeUnliftIO (((forall r. Eff es r -> IO r) -> IO a) -> Eff es a)
-> ((forall r. Eff es r -> IO r) -> IO a) -> Eff es a
forall a b. (a -> b) -> a -> b
$ \forall r. Eff es r -> IO r
unlift -> do
  let unliftHandler :: Handler (Eff es) a -> Handler a
unliftHandler (C.Handler e -> Eff es a
handler) = (e -> IO a) -> Handler a
forall a e. Exception e => (e -> IO a) -> Handler a
E.Handler (Eff es a -> IO a
forall r. Eff es r -> IO r
unlift (Eff es a -> IO a) -> (e -> Eff es a) -> e -> IO a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. e -> Eff es a
handler)
  IO a -> [Handler a] -> IO a
forall a. IO a -> [Handler a] -> IO a
E.catches (Eff es a -> IO a
forall r. Eff es r -> IO r
unlift Eff es a
action) ((Handler (Eff es) a -> Handler a)
-> [Handler (Eff es) a] -> [Handler a]
forall a b. (a -> b) -> [a] -> [b]
map Handler (Eff es) a -> Handler a
unliftHandler [Handler (Eff es) a]
handlers)

-- | A variant of 'catches' that fully forces evaluation of the result value to
-- find all impure exceptions.
catchesDeep
  :: NFData a
  => Eff es a
  -> [C.Handler (Eff es) a]
  -- ^ The exception handlers.
  -> Eff es a
catchesDeep :: forall a (es :: [Effect]).
NFData a =>
Eff es a -> [Handler (Eff es) a] -> Eff es a
catchesDeep Eff es a
action = Eff es a -> [Handler (Eff es) a] -> Eff es a
forall (es :: [Effect]) a.
Eff es a -> [Handler (Eff es) a] -> Eff es a
catches (a -> Eff es a
forall a (es :: [Effect]). NFData a => a -> Eff es a
evaluateDeep (a -> Eff es a) -> Eff es a -> Eff es a
forall (m :: Type -> Type) a b. Monad m => (a -> m b) -> m a -> m b
=<< Eff es a
action)

----------------------------------------
-- Cleanup

-- | Lifted 'E.bracket'.
bracket
  :: Eff es a
  -- ^ Computation to run first.
  -> (a -> Eff es b)
  -- ^ Computation to run last.
  -> (a -> Eff es c)
  -- ^ Computation to run in-between.
  -> Eff es c
bracket :: forall (es :: [Effect]) a b c.
Eff es a -> (a -> Eff es b) -> (a -> Eff es c) -> Eff es c
bracket Eff es a
before a -> Eff es b
after a -> Eff es c
action = ((forall r. Eff es r -> IO r) -> IO c) -> Eff es c
forall (es :: [Effect]) a.
((forall r. Eff es r -> IO r) -> IO a) -> Eff es a
reallyUnsafeUnliftIO (((forall r. Eff es r -> IO r) -> IO c) -> Eff es c)
-> ((forall r. Eff es r -> IO r) -> IO c) -> Eff es c
forall a b. (a -> b) -> a -> b
$ \forall r. Eff es r -> IO r
unlift -> do
  IO a -> (a -> IO b) -> (a -> IO c) -> IO c
forall a b c. IO a -> (a -> IO b) -> (a -> IO c) -> IO c
E.bracket (Eff es a -> IO a
forall r. Eff es r -> IO r
unlift Eff es a
before) (Eff es b -> IO b
forall r. Eff es r -> IO r
unlift (Eff es b -> IO b) -> (a -> Eff es b) -> a -> IO b
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> Eff es b
after) (Eff es c -> IO c
forall r. Eff es r -> IO r
unlift (Eff es c -> IO c) -> (a -> Eff es c) -> a -> IO c
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> Eff es c
action)

-- | Lifted 'E.bracket_'.
bracket_
  :: Eff es a
  -- ^ Computation to run first.
  -> Eff es b
  -- ^ Computation to run last.
  -> Eff es c
  -- ^ Computation to run in-between.
  -> Eff es c
bracket_ :: forall (es :: [Effect]) a b c.
Eff es a -> Eff es b -> Eff es c -> Eff es c
bracket_ Eff es a
before Eff es b
after Eff es c
action = ((forall r. Eff es r -> IO r) -> IO c) -> Eff es c
forall (es :: [Effect]) a.
((forall r. Eff es r -> IO r) -> IO a) -> Eff es a
reallyUnsafeUnliftIO (((forall r. Eff es r -> IO r) -> IO c) -> Eff es c)
-> ((forall r. Eff es r -> IO r) -> IO c) -> Eff es c
forall a b. (a -> b) -> a -> b
$ \forall r. Eff es r -> IO r
unlift -> do
  IO a -> IO b -> IO c -> IO c
forall a b c. IO a -> IO b -> IO c -> IO c
E.bracket_ (Eff es a -> IO a
forall r. Eff es r -> IO r
unlift Eff es a
before) (Eff es b -> IO b
forall r. Eff es r -> IO r
unlift Eff es b
after) (Eff es c -> IO c
forall r. Eff es r -> IO r
unlift Eff es c
action)

-- | Lifted 'E.bracketOnError'.
bracketOnError
  :: Eff es a
  -- ^ Computation to run first.
  -> (a -> Eff es b)
  -- ^ Computation to run last when an exception or
  -- t'Effectful.Error.Static.Error' was thrown.
  -> (a -> Eff es c)
  -- ^ Computation to run in-between.
  -> Eff es c
bracketOnError :: forall (es :: [Effect]) a b c.
Eff es a -> (a -> Eff es b) -> (a -> Eff es c) -> Eff es c
bracketOnError Eff es a
before a -> Eff es b
after a -> Eff es c
action = ((forall r. Eff es r -> IO r) -> IO c) -> Eff es c
forall (es :: [Effect]) a.
((forall r. Eff es r -> IO r) -> IO a) -> Eff es a
reallyUnsafeUnliftIO (((forall r. Eff es r -> IO r) -> IO c) -> Eff es c)
-> ((forall r. Eff es r -> IO r) -> IO c) -> Eff es c
forall a b. (a -> b) -> a -> b
$ \forall r. Eff es r -> IO r
unlift -> do
  IO a -> (a -> IO b) -> (a -> IO c) -> IO c
forall a b c. IO a -> (a -> IO b) -> (a -> IO c) -> IO c
E.bracketOnError (Eff es a -> IO a
forall r. Eff es r -> IO r
unlift Eff es a
before) (Eff es b -> IO b
forall r. Eff es r -> IO r
unlift (Eff es b -> IO b) -> (a -> Eff es b) -> a -> IO b
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> Eff es b
after) (Eff es c -> IO c
forall r. Eff es r -> IO r
unlift (Eff es c -> IO c) -> (a -> Eff es c) -> a -> IO c
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> Eff es c
action)

-- | Generalization of 'bracket'.
--
-- See 'C.generalBracket' for more information.
generalBracket
  :: Eff es a
  -- ^ Computation to run first.
  -> (a -> C.ExitCase c -> Eff es b)
  -- ^ Computation to run last.
  -> (a -> Eff es c)
  -- ^ Computation to run in-between.
  -> Eff es (c, b)
generalBracket :: forall (es :: [Effect]) a c b.
Eff es a
-> (a -> ExitCase c -> Eff es b)
-> (a -> Eff es c)
-> Eff es (c, b)
generalBracket = Eff es a
-> (a -> ExitCase c -> Eff es b)
-> (a -> Eff es c)
-> Eff es (c, b)
forall a b c.
HasCallStack =>
Eff es a
-> (a -> ExitCase b -> Eff es c)
-> (a -> Eff es b)
-> Eff es (b, c)
forall (m :: Type -> Type) a b c.
(MonadMask m, HasCallStack) =>
m a -> (a -> ExitCase b -> m c) -> (a -> m b) -> m (b, c)
C.generalBracket

-- | Lifted 'E.finally'.
finally
  :: Eff es a
  -> Eff es b
  -- ^ Computation to run last.
  -> Eff es a
finally :: forall (es :: [Effect]) a b. Eff es a -> Eff es b -> Eff es a
finally Eff es a
action Eff es b
handler = ((forall r. Eff es r -> IO r) -> IO a) -> Eff es a
forall (es :: [Effect]) a.
((forall r. Eff es r -> IO r) -> IO a) -> Eff es a
reallyUnsafeUnliftIO (((forall r. Eff es r -> IO r) -> IO a) -> Eff es a)
-> ((forall r. Eff es r -> IO r) -> IO a) -> Eff es a
forall a b. (a -> b) -> a -> b
$ \forall r. Eff es r -> IO r
unlift -> do
  IO a -> IO b -> IO a
forall a b. IO a -> IO b -> IO a
E.finally (Eff es a -> IO a
forall r. Eff es r -> IO r
unlift Eff es a
action) (Eff es b -> IO b
forall r. Eff es r -> IO r
unlift Eff es b
handler)

-- | Lifted 'E.onException'.
onException
  :: Eff es a
  -> Eff es b
  -- ^ Computation to run last when an exception or
  -- t'Effectful.Error.Static.Error' was thrown.
  -> Eff es a
onException :: forall (es :: [Effect]) a b. Eff es a -> Eff es b -> Eff es a
onException Eff es a
action Eff es b
handler = ((forall r. Eff es r -> IO r) -> IO a) -> Eff es a
forall (es :: [Effect]) a.
((forall r. Eff es r -> IO r) -> IO a) -> Eff es a
reallyUnsafeUnliftIO (((forall r. Eff es r -> IO r) -> IO a) -> Eff es a)
-> ((forall r. Eff es r -> IO r) -> IO a) -> Eff es a
forall a b. (a -> b) -> a -> b
$ \forall r. Eff es r -> IO r
unlift -> do
  IO a -> IO b -> IO a
forall a b. IO a -> IO b -> IO a
E.onException (Eff es a -> IO a
forall r. Eff es r -> IO r
unlift Eff es a
action) (Eff es b -> IO b
forall r. Eff es r -> IO r
unlift Eff es b
handler)

----------------------------------------
-- Utils

-- | Lifted 'E.evaluate'.
evaluate :: a -> Eff es a
evaluate :: forall a (es :: [Effect]). a -> Eff es a
evaluate = IO a -> Eff es a
forall a (es :: [Effect]). IO a -> Eff es a
unsafeEff_ (IO a -> Eff es a) -> (a -> IO a) -> a -> Eff es a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> IO a
forall a. a -> IO a
E.evaluate

-- | Deeply evaluate a value using 'evaluate' and 'NFData'.
evaluateDeep :: NFData a => a -> Eff es a
evaluateDeep :: forall a (es :: [Effect]). NFData a => a -> Eff es a
evaluateDeep = IO a -> Eff es a
forall a (es :: [Effect]). IO a -> Eff es a
unsafeEff_ (IO a -> Eff es a) -> (a -> IO a) -> a -> Eff es a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> IO a
forall a. a -> IO a
E.evaluate (a -> IO a) -> (a -> a) -> a -> IO a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> a
forall a. NFData a => a -> a
force

#if MIN_VERSION_base(4,20,0)
-- | Lifted 'E.annotateIO'.
annotateIO :: E.ExceptionAnnotation e => e -> Eff es a -> Eff es a
annotateIO e action = reallyUnsafeUnliftIO $ \unlift -> do
  E.annotateIO e (unlift action)
#endif

----------------------------------------
-- Check exception type

-- $syncVsAsync
--
-- /Note:/ there's no way to determine whether an exception was thrown
-- synchronously or asynchronously, so these functions rely on a
-- heuristic. Namely, an exception type is determined by its 'E.Exception'
-- instance.
--
-- Exception types with the default 'E.Exception' instance are considered
-- synchronous:
--
-- >>> data SyncEx = SyncEx deriving (Show)
-- >>> instance Exception SyncEx
--
-- >>> isSyncException SyncEx
-- True
--
-- >>> isAsyncException SyncEx
-- False
--
-- Whereas for asynchronous exceptions you need to define their 'E.Exception'
-- instance as follows:
--
-- >>> data AsyncEx = AsyncEx deriving (Show)
-- >>> :{
--   instance Exception AsyncEx where
--     toException = asyncExceptionToException
--     fromException = asyncExceptionFromException
-- :}
--
-- >>> isSyncException AsyncEx
-- False
--
-- >>> isAsyncException AsyncEx
-- True

-- | Check if the given exception is considered synchronous.
isSyncException :: E.Exception e => e -> Bool
isSyncException :: forall e. Exception e => e -> Bool
isSyncException e
e = case SomeException -> Maybe SomeAsyncException
forall e. Exception e => SomeException -> Maybe e
E.fromException (e -> SomeException
forall e. Exception e => e -> SomeException
E.toException e
e) of
  Just E.SomeAsyncException{} -> Bool
False
  Maybe SomeAsyncException
Nothing -> Bool
True

-- | Check if the given exception is considered asynchronous.
isAsyncException :: E.Exception e => e -> Bool
isAsyncException :: forall e. Exception e => e -> Bool
isAsyncException e
e = case SomeException -> Maybe SomeAsyncException
forall e. Exception e => SomeException -> Maybe e
E.fromException (e -> SomeException
forall e. Exception e => e -> SomeException
E.toException e
e) of
  Just E.SomeAsyncException{} -> Bool
True
  Maybe SomeAsyncException
Nothing -> Bool
False

----------------------------------------
-- Low-level API

-- | Lifted 'E.mask'.
mask :: ((forall r. Eff es r -> Eff es r) -> Eff es a) -> Eff es a
mask :: forall (es :: [Effect]) a.
((forall r. Eff es r -> Eff es r) -> Eff es a) -> Eff es a
mask (forall r. Eff es r -> Eff es r) -> Eff es a
k = ((forall r. Eff es r -> IO r) -> IO a) -> Eff es a
forall (es :: [Effect]) a.
((forall r. Eff es r -> IO r) -> IO a) -> Eff es a
reallyUnsafeUnliftIO (((forall r. Eff es r -> IO r) -> IO a) -> Eff es a)
-> ((forall r. Eff es r -> IO r) -> IO a) -> Eff es a
forall a b. (a -> b) -> a -> b
$ \forall r. Eff es r -> IO r
unlift -> do
  ((forall a. IO a -> IO a) -> IO a) -> IO a
forall b. ((forall a. IO a -> IO a) -> IO b) -> IO b
E.mask (((forall a. IO a -> IO a) -> IO a) -> IO a)
-> ((forall a. IO a -> IO a) -> IO a) -> IO a
forall a b. (a -> b) -> a -> b
$ \forall a. IO a -> IO a
release -> Eff es a -> IO a
forall r. Eff es r -> IO r
unlift (Eff es a -> IO a) -> Eff es a -> IO a
forall a b. (a -> b) -> a -> b
$ (forall r. Eff es r -> Eff es r) -> Eff es a
k ((IO r -> IO r) -> Eff es r -> Eff es r
forall a b (es :: [Effect]). (IO a -> IO b) -> Eff es a -> Eff es b
reallyUnsafeLiftMapIO IO r -> IO r
forall a. IO a -> IO a
release)

-- | Lifted 'E.mask_'.
mask_ :: Eff es a -> Eff es a
mask_ :: forall (es :: [Effect]) a. Eff es a -> Eff es a
mask_ Eff es a
action = ((forall r. Eff es r -> IO r) -> IO a) -> Eff es a
forall (es :: [Effect]) a.
((forall r. Eff es r -> IO r) -> IO a) -> Eff es a
reallyUnsafeUnliftIO (((forall r. Eff es r -> IO r) -> IO a) -> Eff es a)
-> ((forall r. Eff es r -> IO r) -> IO a) -> Eff es a
forall a b. (a -> b) -> a -> b
$ \forall r. Eff es r -> IO r
unlift -> do
  IO a -> IO a
forall a. IO a -> IO a
E.mask_ (Eff es a -> IO a
forall r. Eff es r -> IO r
unlift Eff es a
action)

-- | Lifted 'E.uninterruptibleMask'.
uninterruptibleMask :: ((forall r. Eff es r -> Eff es r) -> Eff es a) -> Eff es a
uninterruptibleMask :: forall (es :: [Effect]) a.
((forall r. Eff es r -> Eff es r) -> Eff es a) -> Eff es a
uninterruptibleMask (forall r. Eff es r -> Eff es r) -> Eff es a
k = ((forall r. Eff es r -> IO r) -> IO a) -> Eff es a
forall (es :: [Effect]) a.
((forall r. Eff es r -> IO r) -> IO a) -> Eff es a
reallyUnsafeUnliftIO (((forall r. Eff es r -> IO r) -> IO a) -> Eff es a)
-> ((forall r. Eff es r -> IO r) -> IO a) -> Eff es a
forall a b. (a -> b) -> a -> b
$ \forall r. Eff es r -> IO r
unlift -> do
  ((forall a. IO a -> IO a) -> IO a) -> IO a
forall b. ((forall a. IO a -> IO a) -> IO b) -> IO b
E.uninterruptibleMask (((forall a. IO a -> IO a) -> IO a) -> IO a)
-> ((forall a. IO a -> IO a) -> IO a) -> IO a
forall a b. (a -> b) -> a -> b
$ \forall a. IO a -> IO a
release -> Eff es a -> IO a
forall r. Eff es r -> IO r
unlift (Eff es a -> IO a) -> Eff es a -> IO a
forall a b. (a -> b) -> a -> b
$ (forall r. Eff es r -> Eff es r) -> Eff es a
k ((IO r -> IO r) -> Eff es r -> Eff es r
forall a b (es :: [Effect]). (IO a -> IO b) -> Eff es a -> Eff es b
reallyUnsafeLiftMapIO IO r -> IO r
forall a. IO a -> IO a
release)

-- | Lifted 'E.uninterruptibleMask_'.
uninterruptibleMask_ :: Eff es a -> Eff es a
uninterruptibleMask_ :: forall (es :: [Effect]) a. Eff es a -> Eff es a
uninterruptibleMask_ Eff es a
action = ((forall r. Eff es r -> IO r) -> IO a) -> Eff es a
forall (es :: [Effect]) a.
((forall r. Eff es r -> IO r) -> IO a) -> Eff es a
reallyUnsafeUnliftIO (((forall r. Eff es r -> IO r) -> IO a) -> Eff es a)
-> ((forall r. Eff es r -> IO r) -> IO a) -> Eff es a
forall a b. (a -> b) -> a -> b
$ \forall r. Eff es r -> IO r
unlift -> do
  IO a -> IO a
forall a. IO a -> IO a
E.uninterruptibleMask_ (Eff es a -> IO a
forall r. Eff es r -> IO r
unlift Eff es a
action)

-- | Lifted 'E.getMaskingState'.
getMaskingState :: Eff es E.MaskingState
getMaskingState :: forall (es :: [Effect]). Eff es MaskingState
getMaskingState = IO MaskingState -> Eff es MaskingState
forall a (es :: [Effect]). IO a -> Eff es a
unsafeEff_ IO MaskingState
E.getMaskingState

-- | Lifted 'E.interruptible'.
interruptible :: Eff es a -> Eff es a
interruptible :: forall (es :: [Effect]) a. Eff es a -> Eff es a
interruptible Eff es a
action = ((forall r. Eff es r -> IO r) -> IO a) -> Eff es a
forall (es :: [Effect]) a.
((forall r. Eff es r -> IO r) -> IO a) -> Eff es a
reallyUnsafeUnliftIO (((forall r. Eff es r -> IO r) -> IO a) -> Eff es a)
-> ((forall r. Eff es r -> IO r) -> IO a) -> Eff es a
forall a b. (a -> b) -> a -> b
$ \forall r. Eff es r -> IO r
unlift -> do
  IO a -> IO a
forall a. IO a -> IO a
E.interruptible (Eff es a -> IO a
forall r. Eff es r -> IO r
unlift Eff es a
action)

-- | Lifted 'E.allowInterrupt'.
allowInterrupt :: Eff es ()
allowInterrupt :: forall (es :: [Effect]). Eff es ()
allowInterrupt = IO () -> Eff es ()
forall a (es :: [Effect]). IO a -> Eff es a
unsafeEff_ IO ()
E.allowInterrupt

-- $setup
-- >>> import Control.Exception (Exception)
-- >>> import Control.Exception (asyncExceptionFromException)
-- >>> import Control.Exception (asyncExceptionToException)