open import Level

-- some typeclasses
record Apply (M :  {}  Set   Set ) (ℓ₁ ℓ₂ : Level) : Set (suc ℓ₁  suc ℓ₂) where
  field
    _<*>_ :  {t₁ : Set ℓ₁} {t₂ : Set ℓ₂}  M (t₁  t₂)  M t₁  M t₂

record Return (M :  {}  Set   Set ) ( : Level) : Set (suc ) where
  field
    return :  (t : Set )  M t

record Bind (M :  {}  Set   Set ) (ℓ₁ ℓ₂ : Level) : Set (suc ℓ₁  suc ℓ₂) where
  field
    _>>=_ :  {t₁ : Set ℓ₁} {t₂ : Set ℓ₂}  M t₁  (t₁  M t₂)  M t₂

open Apply {{...}}
open Return {{...}}
open Bind {{...}}

-- make a nice free monad

open import Data.Container
open import Data.Container.Combinator hiding (_∘_)

private
  _⋆C_ :  {c} (_ : Container c) {}  Set   Container (c  )
  _⋆C_ {c} (Shape  Position) {} X = const (Lift {} {c} X)  (Lift {c} {} Shape  λ x  Lift {c} {} (Position (lower x)))

  PrimFree :  {ℓc} (c : Container ℓc) {} (t : Set )  Set (ℓc  )
  PrimFree c t = μ (c ⋆C t)

record Free {ℓc} (c : Container ℓc) {} (t : Set ) : Set (ℓc  ) where
  field
    free : PrimFree c t

-- black magic (He Who Must Not Be Named)

data Interpreter {ℓc} (c : Container ℓc) {} (t : Set ) : _ where
  MkInterpreter :  (_ : {M :  {}  Set   Set }
                        {{_ :  {ℓ₁ ℓ₂}  Apply M ℓ₁ ℓ₂}}
                        {{_ :  {}  Return M }}
                        {{_ :  {ℓ₁ ℓ₂}  Bind M ℓ₁ ℓ₂}}
                  ((∀ {} {t' : Set }  Free c t'  M t')  M t))  Interpreter c t

private
  -- helper to pull the interpreter out of our Interpreter
  extract :  {ℓc} {c : Container ℓc} {} {t : Set }  Interpreter c t 
                  {M :  {}  Set   Set }
                        {{_ :  {ℓ₁ ℓ₂}  Apply M ℓ₁ ℓ₂}}
                        {{_ :  {}  Return M }}
                        {{_ :  {ℓ₁ ℓ₂}  Bind M ℓ₁ ℓ₂}}
                  ((∀ {} {t' : Set }  Free c t'  M t')  M t)
  extract (MkInterpreter x) = x

open import Data.W
open import Data.Sum
open import Data.Empty
open import Function

-- shapes from our free monad are still usable in the interpreter
insert :  {ℓ₁ ℓ₂} {t : Set ℓ₁} {c : Container ℓ₂}  (x : Shape c)  Interpreter c (Position c x)
insert {_} {_} {t} {c} v =
  let
    m : Free c (Position c v)
    m = record { free = sup (inj₂ (lift v))  x  sup (inj₁ (lift (Lift.lower x))) (⊥-elim  lower)) }
  in
  MkInterpreter  r  r m)

-- and we can define our ops
pure :  {} {t : Set } {ℓc} {c : Container ℓc}  t  Interpreter c t
pure x = MkInterpreter λ r  r record { free = sup (inj₁ (lift x)) (⊥-elim  lower) }

_<**>_ :  {ℓ₁ ℓ₂} {t₁ : Set ℓ₁} {t₂ : Set ℓ₂} {ℓc} {c : Container ℓc} 
         Interpreter c (t₁  t₂)  Interpreter c t₁  Interpreter c t₂
_<**>_ (MkInterpreter f) (MkInterpreter i) = MkInterpreter  {{w}} r  f r <*> i r)

_>>-_ :  {ℓ₁ ℓ₂} {t₁ : Set ℓ₁} {t₂ : Set ℓ₂} {ℓc} {c : Container ℓc} 
         Interpreter c t₁  (t₁  Interpreter c t₂)  Interpreter c t₂
_>>-_ (MkInterpreter i) f = MkInterpreter  r  i r >>= λ x  extract (f x) r)

-- but if you try to make these instances of our typeclass... ohno!

-- instance
--   interpreterCanBind : ∀ {ℓc} {c : Container ℓc} {ℓ₁ ℓ₂} → Bind (Interpreter c) ℓ₁ ℓ₂
--   interpreterCanBind = ?