packages feed

effectful-0.0.0.0: src/Effectful/Error.hs

-- | Support for checked exceptions.
module Effectful.Error
 ( Error
 , runError
 , throwError
 , catchError
 , tryError
 ) where

import Control.Exception
import Data.Typeable
import GHC.Stack

import Effectful.Internal.Env
import Effectful.Internal.Has
import Effectful.Internal.Monad
import Effectful.Internal.Utils

type role Error nominal
data Error e = Error

runError
  :: forall e es a. Exception e
  => Eff (Error e : es) a
  -> Eff es (Either ([String], e) a)
runError (Eff m) = impureEff $ \es0 -> mask $ \release -> do
  size <- sizeEnv es0
  es <- unsafeConsEnv (Error @e) es0
  try (release $ m es) >>= \case
    Right a           -> Right a      <$ unsafeTrimEnv size es
    Left (WrapE cs e) -> Left (cs, e) <$ unsafeTrimEnv size es

throwError
  :: (HasCallStack, Exception e, Error e :> es)
  => e
  -> Eff es a
throwError e = impureEff_ $ do
  throwIO $ WrapE (ppCallStack <$> getCallStack callStack) e

catchError
  :: (Exception e, Error e :> es)
  => Eff es a
  -> ([String] -> e -> Eff es a)
  -> Eff es a
catchError (Eff m) handler = impureEff $ \es -> do
  size <- sizeEnv es
  m es `catch` \(WrapE e cs) -> do
    checkSizeEnv size es
    unEff (handler e cs) es

tryError
  :: (Exception e, Error e :> es)
  => Eff es a
  -> Eff es (Either ([String], e) a)
tryError m = (Right <$> m) `catchError` \es e -> pure (Left (es, e))

----------------------------------------
-- WrapE

data WrapE e = WrapE [String] e
  deriving Show
instance (Show e, Typeable e) => Exception (WrapE e)