{-# OPTIONS --universe-polymorphism #-}

module FreeAnnotatedMonad where

open import Data.Container as Container
open import Data.Container.Combinator as C using (const[_]⟶_)
open import Data.Empty
open import Data.List as List
open import Data.Product
open import Function
open import Level
open import Relation.Binary.PropositionalEquality as P using (_≡_)

------------------------------------------------------------------------
-- Fluff

-- The identity functor and composition are defined as records to make
-- Agda's type inference work a bit better.

record Id {} (A : Set ) : Set  where
  constructor return
  field ! : id A

record _∘̂_ {} (F G : Set   Set ) (A : Set ) : Set  where
  constructor effect
  field ! : (F  G) A

------------------------------------------------------------------------
-- Free (?) annotated monad

-- The free (?) annotated monad over a sequence of functors
-- (represented as containers).

Free :  {c}  List (Container c)  Set c  Set c
Free []       = Id
Free (C  Cs) =  C  ∘̂ Free Cs

infixl 5 _>>=_

_>>=_ :  { Cs₁ Cs₂} {A B : Set } 
        Free Cs₁ A  (A  Free Cs₂ B)  Free (Cs₁ ++ Cs₂) B
_>>=_ {Cs₁ = []}      (return x) g = g x
_>>=_ {Cs₁ = C  Cs₁} (effect f) g =
  effect (Container.map  x  x >>= g) f)

------------------------------------------------------------------------
-- Equality

-- Equality. Heterogeneous to avoid having to cast types.

infix 4 _≈_

data _≈_ {c} {A : Set c} :  {Cs₁ Cs₂} 
         Free Cs₁ A  Free Cs₂ A  Set (suc c) where
  return-cong :  {x₁ x₂} (x₁≡x₂ : x₁  x₂)  return x₁  return x₂
  effect-cong :  {C Cs₁ Cs₂}
                  {f₁ :  C  (Free Cs₁ A)} {f₂ :  C  (Free Cs₂ A)}
                (f₁≈f₂ : Container.Eq _≈_ f₁ f₂)  effect f₁  effect f₂

refl :   {c Cs} {A : Set c} (f : Free Cs A)  f  f
refl {Cs = []}     (return x) = return-cong P.refl
refl {Cs = C  Cs} (effect f) = effect-cong (P.refl , refl  proj₂ f)

postulate -- Somewhat awkward proofs; not used below.

  sym :  {c Cs₁ Cs₂} {A : Set c} {f₁ : Free Cs₁ A} {f₂ : Free Cs₂ A} 
        f₁  f₂  f₂  f₁

  trans :  {c Cs₁ Cs₂ Cs₃} {A : Set c}
            {f₁ : Free Cs₁ A} {f₂ : Free Cs₂ A} {f₃ : Free Cs₃ A} 
          f₁  f₂  f₂  f₃  f₁  f₃

------------------------------------------------------------------------
-- Monad laws

left-identity :  { Cs} {A B : Set } (x : A) (f : A  Free Cs B) 
                return x >>= f  f x
left-identity x f = refl (f x)

right-identity :  { Cs} {A : Set } (m : Free Cs A) 
                 m >>= return  m
right-identity {Cs = []}     (return x) = refl (return x)
right-identity {Cs = C  Cs} (effect f) =
  effect-cong (P.refl , right-identity  proj₂ f)

assoc :  { Cs₁ Cs₂ Cs₃} {A B C : Set }
        (m : Free Cs₁ A) (g : A  Free Cs₂ B) (h : B  Free Cs₃ C) 
        m >>= g >>= h  m >>= λ x  g x >>= h
assoc {Cs₁ = []}      (return x) g h = refl (g x >>= h)
assoc {Cs₁ = C  Cs₁} (effect f) g h =
  effect-cong (P.refl , λ p  assoc (proj₂ f p) g h)

------------------------------------------------------------------------
-- Example

-- The n-ary function type: _⇨_.

infixr 4 _⇨_

_⇨_ :  {}  List (Set )  Set   Set 
_⇨_ As = Free (List.map  A  const[ A ]⟶ C.id) As)

-- Abstraction.

Λ :  { A As} {B : Set }  (A  As  B)  A  As  B
Λ f = effect (_ , f  proj₁)

-- Application.

infixl 10 _·_

_·_ :  { A As} {B : Set }  A  As  B  A  As  B
effect (_ , f) · x = f (x , _)

-- Extraction of the result.

run :  {} {B : Set }  []  B  B
run (return x) = x