module extra.clos where
import Relation.Binary.PropositionalEquality as Eq
open Eq using (_≡_; refl)
open import Relation.Nullary.Decidable using (True; toWitness)
open import Data.Product using (_×_; _,_; ∃-syntax; Σ-syntax)
open import Data.Sum using (_⊎_; inj₁; inj₂) renaming ([_,_] to case-⊎)
infixr 7 _⇒_
data Type : Set where
`ℕ : Type
_⇒_ : Type → Type → Type
infixl 5 _,-_
data Context : Set where
∅ : Context
_,-_ : Context → Type → Context
infix 4 _∋_
data _∋_ : Context → Type → Set where
Z : ∀ {Γ A} → Γ ,- A ∋ A
S : ∀ {Γ A B} → Γ ∋ A → Γ ,- B ∋ A
infix 4 _⊢_
infix 5 ƛ_
infixl 7 _·_
infix 9 `_
data _⊢_ : Context → Type → Set where
`_ : ∀ {Γ A} → Γ ∋ A → Γ ⊢ A
ƛ_ : ∀ {Γ A B} → Γ ,- A ⊢ B → Γ ⊢ A ⇒ B
_·_ : ∀ {Γ A B} → Γ ⊢ A ⇒ B → Γ ⊢ A → Γ ⊢ B
data ClosEnv : Context → Set
infixl 8 _[_]
data Clos : Type → Set where
_[_] : ∀ {Γ A} → (M : Γ ⊢ A) → ClosEnv Γ → Clos A
_·_ : ∀ {A B} → Clos (A ⇒ B) → Clos A → Clos B
data Value : ∀ {A} → Clos A → Set where
V-ƛ : ∀ {Γ A B γ} {M : Γ ,- A ⊢ B} → Value ((ƛ M) [ γ ])
infixl 5 _,'_
data ClosEnv where
∅' : ClosEnv ∅
_,'_ : ∀ {γ A} → ClosEnv γ → {c : Clos A} → Value c → ClosEnv (γ ,- A)
lookup : ∀ {Γ A} → Γ ∋ A → ClosEnv Γ → Clos A
lookup Z (_,'_ γ {c = c} V) = c
lookup (S x) (γ ,' V) = lookup x γ
infix 4 _—→_
data _—→_ {A} : Clos A → Clos A → Set where
β : ∀ {Γ B γ c} {M : Γ ,- B ⊢ A}
→ (V : Value c)
→ (ƛ M) [ γ ] · c —→ M [ γ ,' V ]
ν : ∀ {B c₁} {c c' : Clos (B ⇒ A)}
→ c —→ c'
→ c · c₁ —→ c' · c₁
μ : ∀ {B c₁ c₁'} {c : Clos (B ⇒ A)}
→ Value c
→ c₁ —→ c₁'
→ c · c₁ —→ c · c₁'
var : ∀ {Γ c x} {γ : ClosEnv Γ}
→ c ≡ lookup x γ
→ (` x) [ γ ] —→ c
app : ∀ {Γ γ B N} {M : Γ ⊢ B ⇒ A}
→ (M · N) [ γ ] —→ M [ γ ] · N [ γ ]
progress : ∀ {A} → (c : Clos A) → Value c ⊎ ∃[ c' ] c —→ c'
progress (` x [ γ ]) = inj₂ (lookup x γ , var refl)
progress ((ƛ M) [ γ ]) = inj₁ V-ƛ
progress ((M · N) [ γ ]) = inj₂ (M [ γ ] · N [ γ ] , app)
progress (c · c₁) with progress c
... | inj₂ (c' , c→c') = inj₂ (c' · c₁ , ν c→c')
... | inj₁ (V-ƛ {γ = γ} {M = M}) with progress c₁
... | inj₁ vc₁ = inj₂ (M [ γ ,' vc₁ ] , β vc₁)
... | inj₂ (c₁' , c₁→c₁') = inj₂ ((ƛ M) [ γ ] · c₁' , μ V-ƛ c₁→c₁')