module Examples.Lam where

open import Data.Product
open import Data.Sum
open import Data.Unit
open import Data.Empty
open import Data.Bool hiding (T)
open import Data.Maybe
open import Function
open import Relation.Binary.PropositionalEquality

open import Data.Container as Cont
open import Data.Container.Combinator hiding (id; _∘_)


open import Prelude Set  A B  B -> A)  c  id)  f g  g  f)

open import Uniform.posIR Set  A B  B -> A)  c  id)  f g  g  f)
open import Uniform.Intro Set  A B  B -> A)  c  id)  f g  g  f)

open import Examples.NestedTypes

-- Identity functor as a container
I : Cont
I = Data.Container.Combinator.id

-- Maybe functor as a container
M : Cont
M = Bool   x  if x then  else )

fromMaybe : {X : Set} -> Maybe X -> Cont.⟦ M  X
fromMaybe (just x) = false ,  _  x)
fromMaybe nothing = true ,  ())

-- Lambda terms as a nested type
LamN : Nest
LamN = K I  (Id  Id)  (Id  K M)

-- and their interpretation via initial algebras of IR+
Lam : Set -> Set
Lam = Cont.⟦ U  LamN ⟫IR  T 

-- the expected constructors are definable (the terms look noisy
-- because of the coproduct encoding):

var : {A : Set} -> A -> Lam A
var a = (intro (true , true , _ ,  ()) ,  ()) , _ , _)) ,  _  a)

app : {A : Set} -> Lam A -> Lam A -> Lam A
app f s
 = (intro (true , false , _ ,  _  proj₁ f) ,  _  proj₁ s) , _ , _)) ,
    { (inj₁ x)  proj₂ s x ; (inj₂ y)  proj₂ f y })

abs : {A : Set} -> Lam (Maybe A) -> Lam A
abs t
 = intro (false , _ , _ ,  _  proj₁ t) ,  ()) , (proj₁  t') , _) ,
    x  proj₂ (t' (proj₁ x)) (proj₂ x))
    where t' = fromMaybe  (proj₂ t)