module Staged.Effects.Except where

open import Function

open import Data.Empty
open import Data.Unit
open import Data.Product
open import Data.Sum
open import Data.Maybe using (Maybe ; just ; nothing)

open import Staged.Denote.Sig
open import Staged.Denote.Tree

open import Relation.Binary.PropositionalEquality hiding ([_])

open import Category.Functor

module _ where

  open Sig
  open RawFunctor  ... 

  data ExceptOp (X : Set) : Set where
    `throw : X  ExceptOp X
    `catch : ExceptOp X

  ExcSig : Set  Set  Sig
  S₁ (ExcSig X V) = ExceptOp X
  P₁ (ExcSig X V) (`throw x) = 
  P₁ (ExcSig X V) `catch     = V
  S₂ (ExcSig X V) (`throw x) = 
  S₂ (ExcSig X V) `catch = Maybe X
  P₂ (ExcSig X V) {s₁ = `throw x} ()
  P₂ (ExcSig X V) {s₁ = `catch} _ = V

  instance
    SumFunctor :  {a} {} {F : Set a  Set a} {X : Set }   RawFunctor F  
                 RawFunctor ((_⊎ X)  F)
    (SumFunctor RawFunctor.<$> f) (inj₁ x) = inj₁ (f <$> x)
    (SumFunctor RawFunctor.<$> f) (inj₂ y) = inj₂ y

  variable X A : Set

  hEx :  {V}  F : RawFunctor L  
        Tree L (ExcSig X V  ζ) A 
        Tree ((_⊎ (L  × X))  L) ζ (A  (L  × X))
  hEx (leaf x) = leaf (inj₁ x)
  hEx (node (inj₁ (`throw x)) l _ _) = leaf (inj₂ (l , x))
  hEx (node (inj₁ `catch) l st k) = do
   m  hEx (st nothing l)
   case m of λ where
     (inj₁ lv)  hEx (k lv)
     (inj₂ (l' , x))  do
       m  hEx (st (just x) (const tt <$> l'))
       case m of λ where
         (inj₁ lv)  hEx (k lv)
         (inj₂ lx)  leaf (inj₂ lx)
  hEx (node (inj₂ y) l st k) =
    node y (inj₁ l)
         (λ{ z (inj₁ x)  hEx (st z x)
           ; z (inj₂ y)  leaf (inj₂ y) })
         λ{ (inj₁ lv)  hEx (k lv)
          ; (inj₂ y)  leaf (inj₂ y) }

  open _⊏_ ⦃...⦄

  throw :  {V}   ExcSig X V  ζ   X  Tree id ζ 
  throw  w  x = node (inj (`throw x)) tt
                        z _  ⊥-elim (subst id (S₂≡  w ⦄) z))
                        r    return (subst id (P₁≡  w ⦄) r))

  catch :  {V}   ExcSig X V  ζ  
          Tree id ζ V  (X  Tree id ζ V)  Tree id ζ V
  catch  w  p h = node (inj `catch) tt
                          z l' 
                           case (subst id (S₂≡  w ⦄) z) of λ where
                             nothing 
                               subst (Tree id _) (P₂≡  w ⦄) p
                             (just x) 
                               subst (Tree id _) (P₂≡  w ⦄) (h x))
                          r  return (subst id (P₁≡  w ⦄) r))