module Control.Monad.Free.Effects where

open import Prelude
open import Data.Set.Definition
open import Data.Set.Member
open import Data.Set.Union
open import Data.Set.Empty
open import Container renaming (⟦_⟧ to ⟦_⟧′)

module _ (Univ : Type) (univ : Univ  Container) (_≟_ : Discrete Univ) where
  open WithDecEq _≟_

  ⟦_⟧ : Univ  Type  Type
  ⟦_⟧ = ⟦_⟧′  univ

  private
    variable
      F G : Univ
      Fs Gs : 𝒦 Univ

  mutual
    Free : 𝒦 Univ  Type  Type
    Free = flip Free′ 

    data Free′ (A : Type) : 𝒦 Univ  Type where
      ret : A  Free  A 
      op :  F  (Free Fs A)  Free (F  Fs) A

  open import Algebra

  module MonadInst where
    _>>=_ : Free Fs A  (A  Free Gs B)  Free (Fs  Gs) B
    ret x >>= k = k x
    op x  >>= k = op (cmap (_>>= k) x)

    extract′ : Empty Fs  Free Fs A  A
    extract′ p (ret x) = x

    extract : Free  A  A
    extract = extract′ tt

  module _ {𝐹 : Type  Type} (mon : Monad 𝐹) where
    open Monad mon

    module _ (traverse :  {F A B}  (A  𝐹 B)   F  A  𝐹 ( F  B)) where
      module _ (E : Univ) where
        interp : ( E   𝐹)  Free Fs A  𝐹 (Free (Fs \\ E) A)
        interp ψ (ret x) = return (ret x)
        interp ψ (op {F = F} x) with E  F
        ... | no  _   = mmap op (traverse (interp ψ) x)
        ... | yes E≡F = traverse (interp ψ) x >>= subst  G   G  _  𝐹 _) E≡F ψ