yaftee-basic-monads-0.1.0.0: src/Control/Monad/Yaftee/Except.hs
{-# LANGUAGE ImportQualifiedPost #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE ExplicitForAll, TypeApplications #-}
{-# LANGUAGE RequiredTypeArguments #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures, TypeOperators #-}
{-# LANGUAGE FlexibleContexts #-}
{-# OPTIONS_GHC -Wall -fno-warn-tabs #-}
module Control.Monad.Yaftee.Except (
-- * NORMAL
E, throw, catch, run, runExc, runIO, fromIO,
-- * NAMED
Named, throwN, catchN, runN, runExcN, runION, fromION,
-- * TOOLS
fromJust, getLeft, getRight
) where
import GHC.TypeLits
import Control.Monad.Yaftee.Eff qualified as Eff
import Control.Monad.Yaftee.IO qualified as IO
import Control.Monad.HigherFreer qualified as F
import Control.HigherOpenUnion qualified as Union
import Control.Exception qualified as IO
import Data.Kind
import Data.Functor.Identity
import Data.HigherFunctor qualified as HFunctor
import Data.FTCQueue qualified as Q
-- * NORMAL
type E = Named ""
throw :: Union.Member (E e) effs => e -> Eff.E effs i o a
throw = throwN ""
catch :: Union.Member (E e) effs =>
Eff.E effs i o a -> (e -> Eff.E effs i o a) -> Eff.E effs i o a
catch = catchN ""
run :: forall e effs i o a . HFunctor.Loose (Union.U effs) =>
Eff.E (E e ': effs) i o a -> Eff.E effs i o (Either e a)
run = runN
runExc :: forall e e' effs i o a .
(HFunctor.Loose (Union.U effs), Union.Member (E e') effs) =>
(e -> e') -> (e' -> e) -> Eff.E (E e ': effs) i o a -> Eff.E effs i o a
runExc = runExcN ""
runIO :: IO.Exception e => Eff.E '[E e, IO.I] i o a -> Eff.E '[IO.I] i o a
runIO = runION
fromIO :: forall e ->
(IO.Exception e, Union.Member (E e) es, Union.Base IO.I es) =>
IO a -> Eff.E es i o a
fromIO e = fromION "" e
-- * NAMED
data Named (nm :: Symbol) e (f :: Type -> Type -> Type -> Type) i o a where
Throw :: forall nm e f a i o . e -> Named nm e f i o a
Catch :: forall nm e f a i o .
f i o a -> (e -> f i o a) -> Named nm e f i o a
instance HFunctor.Tight (Named nm e) where
mapT _ _ (Throw e) = Throw e
mapT f _ (m `Catch` h) = (f m) `Catch` \e -> f $ h e
instance HFunctor.Loose (Named nm e)
throwN :: forall nm -> Union.Member (Named nm e) effs => e -> Eff.E effs i o a
throwN nm = Eff.effh . Throw @nm
catchN :: forall nm -> Union.Member (Named nm e) effs =>
Eff.E effs i o a -> (e -> Eff.E effs i o a) -> Eff.E effs i o a
catchN nm = (Eff.effh .) . Catch @nm
runN :: forall nm e effs i o a . HFunctor.Loose (Union.U effs) =>
Eff.E (Named nm e ': effs) i o a -> Eff.E effs i o (Either e a)
runN = \case
F.Pure x -> F.Pure $ Right x
u F.:>>= q -> case Union.decomp u of
Left u' -> HFunctor.map runN Right u' F.:>>=
Q.singleton (either (F.Pure . Left) (runN F.. q))
Right (Throw e) -> F.Pure $ Left e
Right (m `Catch` h) -> either (F.Pure . Left) (runN F.. q)
=<< either (runN . h) (F.Pure . Right) =<< runN m
runExcN :: forall nm e e' effs i o a . forall nm' ->
(HFunctor.Loose (Union.U effs), Union.Member (Named nm' e') effs) =>
(e -> e') -> (e' -> e) ->
Eff.E (Named nm e ': effs) i o a -> Eff.E effs i o a
runExcN nm' c c' = \case
F.Pure x -> F.Pure x
u F.:>>= q -> case Union.decomp u of
Left u' -> HFunctor.map ((Identity <$>) . runExcN nm' c c')
Identity u' F.:>>=
Q.singleton ((runExcN nm' c c' F.. q) . runIdentity)
Right (Throw e) -> throwN nm' $ c e
Right (m `Catch` h) -> runExcN nm' c c' F.. q =<< catchN nm'
(runExcN nm' c c' m) (runExcN nm' c c' . h . c')
runION :: IO.Exception e =>
Eff.E '[Named nm e, IO.I] i o a -> Eff.E '[IO.I] i o a
runION = \case
F.Pure x -> F.Pure x
u F.:>>= q -> case Union.decomp u of
Left u' -> HFunctor.map ((Identity <$>) . runION) Identity u'
F.:>>= Q.singleton ((runION F.. q) . runIdentity)
Right (Throw e) -> Eff.effBase $ IO.throwIO e
Right (m `Catch` h) ->
runION F.. q =<< runION m `cch` (runION . h)
where m `cch` h = Eff.effBase $ Eff.runM m `IO.catch` (Eff.runM . h)
fromJust :: Union.Member (E e) es => e -> Maybe a -> Eff.E es i o a
fromJust e = \case Nothing -> throw e; Just x -> pure x
getLeft :: Union.Member (E e) es => e -> Either a b -> Eff.E es i o a
getLeft e = \case Left r -> pure r; Right _ -> throw e
getRight :: Union.Member (E e) es => e -> Either a b -> Eff.E es i o b
getRight e = \case Left _ -> throw e; Right r -> pure r
fromION :: forall (nm :: Symbol) -> forall e ->
(IO.Exception e, Union.Member (Named nm e) es, Union.Base IO.I es) =>
IO r -> Eff.E es i o r
fromION nm e act = either (throwN nm) pure =<< Eff.effBase (IO.try @e act)