module Yael.Eff.Error where

import Yael.Eff
import qualified Control.Monad.Except as E
import qualified Control.Exception.Safe as Ex

data Error e m = Error
  { _throw :: forall a . e -> m a
  , _catch :: forall a . m a -> (e -> m a) -> m a
  }

throw :: e -> a :+ '[Error e]
throw e = withEffT $ \Error{_throw} -> _throw e

catch
  :: (HasEff (Error e) f m)
  => EffT f m a
  -> (e -> EffT f m a)
  -> EffT f m a
catch m f = withEffT' $ \lower Error{_catch} ->
  _catch (lower m) (lower . f)

type UncheckedError = Error Ex.SomeException

mtlError :: E.MonadError e m => Error e m
mtlError = Error
  { _throw = E.throwError
  , _catch = E.catchError
  }

exceptionError
  :: (Ex.Exception e, Ex.MonadThrow m, Ex.MonadCatch m)
  => Error e m
exceptionError = Error
  { _throw = Ex.throw
  , _catch = Ex.catch
  }

throwAny :: Ex.Exception e => e -> a :+ '[UncheckedError]
throwAny = throw . Ex.toException

try :: (HasEff (Error e) f m) => EffT f m a -> EffT f m (Either e a)
try m = (Right <$> m) `catch` (return . Left)