packages feed

hw-polysemy-0.2.14.7: core/HaskellWorks/Polysemy/Except.hs

{-# LANGUAGE GADTs           #-}
{-# LANGUAGE TemplateHaskell #-}

-- | Description: The 'Except' effect, providing catch and throw functionality over the final
-- monad via MonadCatch.
module HaskellWorks.Polysemy.Except
  ( -- * Effect
    Except (..),

    -- * Actions
    catchEx,
    throwEx,

    -- * Interpretations
    catchExToFinal,
    catchExToFinalIO,
  ) where

import           Control.Exception    (Exception (..))
import qualified Control.Monad.Catch  as X
import           HaskellWorks.Prelude
import           Polysemy
import           Polysemy.Final

------------------------------------------------------------------------------
-- | An effect capable of providing 'X.catch' and 'X.throwM' semantics. Interpreters for
-- this will successfully run the catch the exceptions thrown in the IO monad.
data Except m a where
  CatchEx
    :: Exception e
    => m a
    -> (e -> m a)
    -> Except m a

  ThrowEx
    :: Exception e
    => e
    -> Except m a

makeSem ''Except

------------------------------------------------------------------------------
-- | Run a 'Except' effect in terms of 'X.catch' and 'X.throwM' through final monad.
--
-- /Beware/: Effects that aren't interpreted in terms of 'IO'
-- will have local state semantics in regards to 'Except' effects
-- interpreted this way. See 'Final'.
catchExToFinal :: forall a r m. ()
  => X.MonadCatch m
  => X.MonadThrow m
  => Member (Final m) r
  => Sem (Except ': r) a
  -> Sem r a
catchExToFinal = interpretFinal $ \case
  CatchEx f h -> do
    s  <- getInitialStateS
    a  <- runS f
    h' <- bindS h
    pure $ X.catch a $ \e -> h' $ e <$ s
  ThrowEx e ->
    pure $ X.throwM e
{-# INLINE catchExToFinal #-}

------------------------------------------------------------------------------
-- | Run a 'Except' effect in terms of 'X.catch' and 'X.throwM' through final IO monad.
catchExToFinalIO :: forall a r. ()
  => Member (Final IO) r
  => Sem (Except ': r) a
  -> Sem r a
catchExToFinalIO = catchExToFinal
{-# INLINE catchExToFinalIO #-}