-- Normalization by evaluation.
--
-- We use an instance of the Kripke model for STLC
-- where worlds are contexts, future is weakening,
-- and forcing `Γ ⊩ a` at base type `a` in world `Γ`
-- is the set of normal forms of type `a` in context `Γ`.
--
-- The soundness theorem gives us for each term `Γ ⊢ a`
-- a semantic value `Γ ⊩ a`, and a function `reify`
-- defined by induction on `a` turns this into a normal
-- form `Γ ⊢ₙ a` (that is the secret sauce).
-- In combination, we get a function mapping terms to normal forms.
module Lecture8NoPeirce where
open import Data.Empty using (⊥; ⊥-elim)
open import Data.String using (String)
open import Relation.Binary.PropositionalEquality as ≡ using (_≢_)
-- Simple types.
BaseTy = String
data Ty : Set where
`_ : (α : BaseTy) → Ty
_⇒_ : (t₁ t₂ : Ty) → Ty
infixr 10 `_
infixr 9 _⇒_
-- Typing contexts.
data Context : Set where
ε : Context
_∙_ : (Γ : Context) (a : Ty) → Context
infixl 5 _∙_
private
variable
α β : BaseTy
a b c : Ty
Γ Δ Φ Ξ : Context
-- De Bruijn indices: a variable points to a type in the context.
data _∈_ (a : Ty) : Context → Set where
zero : a ∈ (Γ ∙ a)
suc : (x : a ∈ Γ) → a ∈ (Γ ∙ b)
-- Well-typed terms of the simply-typed lambda-calculus.
infix 4 _⊢_
data _⊢_ (Γ : Context) : Ty → Set where
var : (x : a ∈ Γ) → Γ ⊢ a
abs : (t : Γ ∙ a ⊢ b) → Γ ⊢ a ⇒ b
app : (t : Γ ⊢ a ⇒ b) (u : Γ ⊢ a) → Γ ⊢ b
private
variable
x : a ∈ Γ
t t' t'' u u' : Γ ⊢ a
------------------------------------------------------------------------
-- Kripke models
-- A Kripke model is characterized by a preorder of worlds and
-- a relation (forcing) monotone in worlds
-- that determines which atoms hold at which world.
record KripkeStructure : Set₁ where
field
-- A set of worlds.
World : Set
-- A preorder on worlds.
-- w ≤ w' shall mean w is a future of w'.
_≤_ : (w w' : World) → Set
refl : ∀{w} → w ≤ w
trans : ∀{w₁ w₂ w₃} → w₁ ≤ w₂ → w₂ ≤ w₃ → w₁ ≤ w₃
-- A forcing relation on atoms.
_⊩ₐ_ : World → BaseTy → Set
-- Forcing is monotone: true atoms stay true in any future.
monₐ : ∀{w w' a} → w ≤ w' → w' ⊩ₐ a → w ⊩ₐ a
module Model (Structure : KripkeStructure) (open KripkeStructure Structure) where
-- The forcing relation extends forcing for atoms to all formulas.
-- Implication A ⇒ B shall only hold in world w
-- if the meta-implication (_⊩ A → _⊩ B) holds not just in w but also
-- in all of its futures w' ≤ w.
-- This promotes implications from mere coincidences
-- to laws of consequence.
private variable
w w' : World
infix 8 _⊩_
_⊩_ : (w : World) (a : Ty) → Set
w ⊩ (` α) = w ⊩ₐ α
w ⊩ (a ⇒ b) = ∀{w'} → w' ≤ w → w' ⊩ a → w' ⊩ b
-- The forcing relation stays monotone.
-- This is proven by induction on the formula.
mon : ∀{w w' a} (ρ : w ≤ w') (v : w' ⊩ a) → w ⊩ a
mon {a = ` α } ρ v = monₐ ρ v
mon {a = a ⇒ b} ρ f = λ ρ' v → f (trans ρ' ρ) v
------------------------------------------------------------------------
-- Soundness: each derivable formula is valid in each Kripke model
module Soundness (Structure : KripkeStructure) where
open KripkeStructure Structure
open Model Structure
private variable w w' : World
-- We extend forcing to contexts: each formula in the context is forced.
data _⊩ₛ_ (w : World) : (Γ : Context) → Set where
ε : w ⊩ₛ ε
_∙_ : (δ : w ⊩ₛ Γ) (v : w ⊩ a) → w ⊩ₛ Γ ∙ a
infixl 5 _∙_
infix 4 _⊩ₛ_ _⊧_
monₛ : (ρ : w ≤ w') (δ : w' ⊩ₛ Γ) → w ⊩ₛ Γ
monₛ ρ ε = ε
monₛ ρ (_∙_ {a = a} δ v) = monₛ ρ δ ∙ mon {a = a} ρ v
-- A sequent Γ ⊢ A is valid in the given Structure
-- if all worlds that force Γ also force A.
_⊧_ : (Γ : Context) (a : Ty) → Set
Γ ⊧ a = ∀{w} → w ⊩ₛ Γ → w ⊩ a
-- Lemma: assumptions are valid (just a lookup).
⦅_⦆ₓ : a ∈ Γ → Γ ⊧ a
⦅ zero ⦆ₓ (ρ ∙ v) = v
⦅ suc x ⦆ₓ (ρ ∙ v) = ⦅ x ⦆ₓ ρ
-- Lemma: application is valid
apply : Γ ⊧ a ⇒ b → Γ ⊧ a → Γ ⊧ b
apply f g ρ = f ρ refl (g ρ)
-- Soundness: all derivable sequents Γ ⊢ A are valid.
⦅_⦆ : Γ ⊢ a → Γ ⊧ a
⦅ var x ⦆ ρ = ⦅ x ⦆ₓ ρ
⦅ app t u ⦆ ρ = ⦅ t ⦆ ρ refl (⦅ u ⦆ ρ)
⦅ abs t ⦆ ρ = λ η a → ⦅ t ⦆ (monₛ η ρ ∙ a)
------------------------------------------------------------------------
-- "Weakening": transporting terms to larger contexts
-- ρ : Wk Γ Δ transports a term living in context Δ to a larger context Γ.
-- The relative order of assumptions in Δ is preserved in Γ;
-- Γ is Δ with additional assumptions inserted anywhere.
--
-- In other words, Δ can be obtained by deleting some assumptions from Γ.
-- This perspective is taken by the constructor names, describing how Δ
-- is stepwise constructed from Γ, by either keeping or skipping assumptions.
data _≤_ : (Γ Δ : Context) → Set where
done : ε ≤ ε
skip : (ρ : Γ ≤ Δ) → (Γ ∙ a) ≤ Δ
keep : (ρ : Γ ≤ Δ) → (Γ ∙ a) ≤ (Δ ∙ a)
private
variable
ρ : Γ ≤ Δ
-- If Γ ≤ Δ, then any assumption a ∈ Δ is also in Γ.
-- weakₓ (ρ : Γ ≤ Δ) transport de Bruijn indices a ∈ Δ to those a ∈ Γ.
-- In particular, "skip" increases a de Bruijn index.
weakₓ : Γ ≤ Δ → a ∈ Δ → a ∈ Γ
weakₓ (keep ρ) zero = zero
weakₓ (keep ρ) (suc x) = suc (weakₓ ρ x)
weakₓ (skip ρ) x = suc (weakₓ ρ x)
-- wk (ρ : Wk Γ Δ) transports a term from context Δ to larger context Γ
-- by updating the de Bruijn indices.
-- In particular, it maps a term t : Term Δ a to a term wk ρ t : Term Γ a.
wk : Γ ≤ Δ → Δ ⊢ a → Γ ⊢ a
wk ρ (var x) = var (weakₓ ρ x)
wk ρ (abs t) = abs (wk (keep ρ) t)
wk ρ (app t u) = app (wk ρ t) (wk ρ u)
-- Weakening compose, so they form a category with identity idW.
idW : Γ ≤ Γ
idW {Γ = ε} = done
idW {Γ = Γ ∙ _} = keep idW
-- We write composition in the diagrammatic order, so that
-- wk (compWW ρ ρ') = wk ρ ∘ wk ρ'.
compWW : Γ ≤ Δ → Δ ≤ Φ → Γ ≤ Φ
compWW done ρ' = ρ'
compWW (skip ρ) ρ' = skip (compWW ρ ρ')
compWW (keep ρ) (skip ρ') = skip (compWW ρ ρ')
compWW (keep ρ) (keep ρ') = keep (compWW ρ ρ')
-- Some shorthands for common weakenings and weakening operations.
skip1 : (Γ ∙ a) ≤ Γ
skip1 = skip idW
wk1 : Γ ⊢ b → Γ ∙ a ⊢ b
wk1 = wk skip1
------------------------------------------------------------------------
-- Normal forms
module NormalForms where
mutual
infix 4 _⊢ᵤ_ _⊢ₙ_
-- Variable headed normal forms: never reduce even under further applications.
-- These are also called neutral.
data _⊢ᵤ_ (Γ : Context) : (a : Ty) → Set where
var : (x : a ∈ Γ) → Γ ⊢ᵤ a
app : (u : Γ ⊢ᵤ a ⇒ b) (n : Γ ⊢ₙ a) → Γ ⊢ᵤ b
-- Normal forms (unless applied to further arguments).
data _⊢ₙ_ (Γ : Context) : (a : Ty) → Set where
-- Neutrals are normal
ne : (u : Γ ⊢ᵤ a) → Γ ⊢ₙ a
abs : (t : Γ ∙ a ⊢ₙ b) → Γ ⊢ₙ a ⇒ b
-- Normal forms are closed under weakening.
mutual
weakᵤ : (ρ : Γ ≤ Δ) (u : Δ ⊢ᵤ a) → Γ ⊢ᵤ a
weakᵤ ρ (var x) = var (weakₓ ρ x)
weakᵤ ρ (app u n) = app (weakᵤ ρ u) (weakₙ ρ n)
weakₙ : (ρ : Γ ≤ Δ) (n : Δ ⊢ₙ a) → Γ ⊢ₙ a
weakₙ ρ (ne u) = ne (weakᵤ ρ u)
weakₙ ρ (abs n) = abs (weakₙ (keep ρ) n)
------------------------------------------------------------------------
-- Normalization by evaluation.
-- We instantiate our generic soundness theorem to the Kripke model
-- where worlds are contexts under the preorder of weakening
-- and forcing atoms A in a world Γ is witnessed by a neutral derivation Γ ⊢ᵤ A.
module NbE where
open NormalForms using (_⊢ᵤ_; weakᵤ)
s : KripkeStructure
s = record
{ World = Context
; _≤_ = _≤_
; refl = idW
; trans = compWW
; _⊩ₐ_ = λ Γ X → Γ ⊢ᵤ (` X)
; monₐ = weakᵤ
}
open Model s
open Soundness s
open NormalForms
-- The soundness theorem can be utilized to give us Γ ⊩ A from Γ ⊢ A.
-- We just need to get a normal form Γ ⊢ₙ A out of Γ ⊩ A.
-- This process is called reification.
--
-- For atomic A, this is trivial, because then Γ ⊩ A is Γ ⊢ᵤ A.
-- It is more tricky for non-atomic A.
--
-- Γ ⊩ A ⇒ B is made of functions f : Δ ≤ Γ → Δ ⊩ A → Δ ⊩ B;
-- we have to turn them back into normal forms.
-- We choose Δ = Γ ∙ A and apply f to the weakening skip idʷ : Γ ∙ A ≤ Γ
-- and some argument Γ ∙ A ⊩ A.
-- This gives us Γ ∙ A ⊩ B which we recursively turn into a normal form Γ ∙ A ⊢ₙ B
-- and with the abstraction theorem for normal forms we get Γ ⊢ₙ A ⇒ B.
--
-- The question is where we get Γ ∙ A ⊩ A from.
-- We know that there is a trivial, even neutral derivation Γ ∙ A ⊢ᵤ A;
-- it is just the last variable ("here").
-- We want to "reflect" it into the model.
--
-- If A is atomic, we thus already have Γ ∙ A ⊩ A.
-- Again, it is more tricky for implications.
--
-- We more generally define reflection of neutral derivations Γ ⊢ᵤ A into the model Γ ⊩ A.
-- In case of implication, we have Γ ⊢ᵤ A ⇒ B and need to produce Δ ⊩ B for any Δ ≤ Γ and Δ ⊩ A.
-- We weaken the neutral derivation to Δ ⊢ᵤ A ⇒ B and apply it to the reification Δ ⊢ₙ A of Δ ⊩ A.
-- Thus we have a neutral Δ ⊢ᵤ B that we can recursively reflect to get Δ ⊩ B.
--
-- Reflection and reification are defined mutually by induction on the formula.
mutual
reify : Γ ⊩ a → Γ ⊢ₙ a
reify {a = ` X} u = ne u
reify {a = a ⇒ b} f = abs (reify (f (skip idW) (reflect {a = a} (var zero))))
reflect : Γ ⊢ᵤ a → Γ ⊩ a
reflect {a = ` X} u = u
reflect {a = a ⇒ b} u ρ v = reflect (app (weakᵤ ρ u) (reify v))
-- An identity environment in the model is needed to instantiate the soundness theorem.
-- It is constructed by reflecting each assumption in Γ, using weakening to make the induction on Γ work.
⊩ₛid : Γ ⊩ₛ Γ
⊩ₛid {Γ = ε} = ε
⊩ₛid {Γ = Γ ∙ a} = monₛ (skip idW) (⊩ₛid {Γ = Γ}) ∙ reflect {a = a} (var zero)
-- For each derivation there is also a normal one.
-- This is now just evaluation (soundness) in the reflected identity environment
-- followed by reification.
norm : Γ ⊢ a → Γ ⊢ₙ a
norm t = reify (⦅ t ⦆ ⊩ₛid)