{-# LANGUAGE
  BangPatterns,
  GADTs,
  RankNTypes,
  ScopedTypeVariables,
  StandaloneKindSignatures,
  TypeOperators #-}

-- | = Exceptions as an algebraic effect
--
-- Variant of "Bluefin.Algae.Exception" that uses dynamic exceptions to cancel
-- continuations.
module Bluefin.Algae.Exception.DynExn
  ( Exception(..)
  , throw
  , catch
  , try
  ) where

import Bluefin.Eff (Eff, type (:&), type (:>))
import Bluefin.Algae.DynExn
import Bluefin.Algae.Exception (Exception(..))
import Bluefin.Exception.Dynamic (DynExn)

-- | Catch an exception.
catch :: forall e a ex zz. ex :> zz =>
  DynExn ex ->
  (forall z. Handler ex (Exception e) z -> Eff (z :& zz) a) ->
  (e -> Eff zz a) ->
  Eff zz a
catch :: forall e a (ex :: Effects) (zz :: Effects).
(ex :> zz) =>
DynExn ex
-> (forall (z :: Effects).
    Handler ex (Exception e) z -> Eff (z :& zz) a)
-> (e -> Eff zz a)
-> Eff zz a
catch DynExn ex
ex forall (z :: Effects).
Handler ex (Exception e) z -> Eff (z :& zz) a
f e -> Eff zz a
h = DynExn ex
-> HandlerBody ex (Exception e) zz a
-> (forall (z :: Effects).
    Handler ex (Exception e) z -> Eff (z :& zz) a)
-> Eff zz a
forall (h :: Effects -> *) (ex :: Effects) (f :: AEffect)
       (ss :: Effects) a.
h ex
-> HandlerBody ex f ss a
-> (forall (s :: Effects). Handler ex f s -> Eff (s :& ss) a)
-> Eff ss a
handle DynExn ex
ex Exception e x -> Continuation ss0 zz x a -> Eff zz a
HandlerBody ex (Exception e) zz a
exceptionHandler Handler ex (Exception e) s -> Eff (s :& zz) a
forall (z :: Effects).
Handler ex (Exception e) z -> Eff (z :& zz) a
f
  where
    exceptionHandler :: HandlerBody ex (Exception e) zz a
    exceptionHandler :: HandlerBody ex (Exception e) zz a
exceptionHandler (Throw e
e) Continuation ss0 zz x a
k = DynExn ex -> Continuation ss0 zz x a -> Eff zz ()
forall (ex :: Effects) (es0 :: Effects) (es :: Effects) b a.
(ex :> es0, ex :> es) =>
DynExn ex -> Continuation es0 es b a -> Eff es ()
cancel DynExn ex
ex Continuation ss0 zz x a
k Eff zz () -> Eff zz a -> Eff zz a
forall a b. Eff zz a -> Eff zz b -> Eff zz b
forall (f :: AEffect) a b. Applicative f => f a -> f b -> f b
*> e -> Eff zz a
h e
e

-- | Return 'Either' the exception or the result of the handled computation.
try :: forall e a ex zz. ex :> zz =>
  DynExn ex ->
  (forall z. Handler ex (Exception e) z -> Eff (z :& zz) a) ->
  Eff zz (Either e a)
try :: forall e a (ex :: Effects) (zz :: Effects).
(ex :> zz) =>
DynExn ex
-> (forall (z :: Effects).
    Handler ex (Exception e) z -> Eff (z :& zz) a)
-> Eff zz (Either e a)
try DynExn ex
ex forall (z :: Effects).
Handler ex (Exception e) z -> Eff (z :& zz) a
f = DynExn ex
-> (forall (z :: Effects).
    Handler ex (Exception e) z -> Eff (z :& zz) (Either e a))
-> (e -> Eff zz (Either e a))
-> Eff zz (Either e a)
forall e a (ex :: Effects) (zz :: Effects).
(ex :> zz) =>
DynExn ex
-> (forall (z :: Effects).
    Handler ex (Exception e) z -> Eff (z :& zz) a)
-> (e -> Eff zz a)
-> Eff zz a
catch DynExn ex
ex ((a -> Either e a) -> Eff (z :& zz) a -> Eff (z :& zz) (Either e a)
forall a b. (a -> b) -> Eff (z :& zz) a -> Eff (z :& zz) b
forall (f :: AEffect) a b. Functor f => (a -> b) -> f a -> f b
fmap a -> Either e a
forall a b. b -> Either a b
Right (Eff (z :& zz) a -> Eff (z :& zz) (Either e a))
-> (Handler ex (Exception e) z -> Eff (z :& zz) a)
-> Handler ex (Exception e) z
-> Eff (z :& zz) (Either e a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handler ex (Exception e) z -> Eff (z :& zz) a
forall (z :: Effects).
Handler ex (Exception e) z -> Eff (z :& zz) a
f) (Either e a -> Eff zz (Either e a)
forall a. a -> Eff zz a
forall (f :: AEffect) a. Applicative f => a -> f a
pure (Either e a -> Eff zz (Either e a))
-> (e -> Either e a) -> e -> Eff zz (Either e a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. e -> Either e a
forall a b. a -> Either a b
Left)

-- | Throw an exception. Call the 'Throw' operation.
throw :: (ex :> zz, z :> zz) => Handler ex (Exception e) z -> e -> Eff zz a
throw :: forall (ex :: Effects) (zz :: Effects) (z :: Effects) e a.
(ex :> zz, z :> zz) =>
Handler ex (Exception e) z -> e -> Eff zz a
throw Handler ex (Exception e) z
h e
e = Handler ex (Exception e) z -> Exception e a -> Eff zz a
forall (ex :: Effects) (es :: Effects) (s :: Effects)
       (f :: AEffect) a.
(ex :> es, s :> es) =>
Handler ex f s -> f a -> Eff es a
call Handler ex (Exception e) z
h (e -> Exception e a
forall e a. e -> Exception e a
Throw e
e)