-- 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 Term.Normalization.NbE where open import Prelude hiding (head) open import Term renaming (Term to _⊢_) open import Term.Weakening renaming (Wk to infix 4 _≤_; lookup to weakₓ) open import Term.KripkeModel private variable Γ Δ Φ : Context α : BaseTy a b c : Ty x : a ∈ Γ t t' t'' u u' : Γ ⊢ a ρ : Γ ≤ Δ ------------------------------------------------------------------------ -- 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) -- Normal spines infix 4 _∣_⊢ₙ_ data _∣_⊢ₙ_ (Γ : Context) : (a : Ty) (c : Ty) → Set where [] : Γ ∣ c ⊢ₙ c _∷_ : (t : Γ ⊢ₙ a) (s : Γ ∣ b ⊢ₙ c) → Γ ∣ a ⇒ b ⊢ₙ c private variable s : Γ ∣ a ⊢ₙ c snoc : (s : Γ ∣ a ⊢ₙ b ⇒ c) (n : Γ ⊢ₙ b) → Γ ∣ a ⊢ₙ c snoc [] n = n ∷ [] snoc (m ∷ s) n = m ∷ snoc s n record NeSpine (Γ : Context) (c : Ty) : Set where field {ty} : Ty head : ty ∈ Γ spine : Γ ∣ ty ⊢ₙ c open NeSpine using (head; spine) neSpine : Γ ⊢ᵤ c → NeSpine Γ c neSpine (var x) = record{ head = x; spine = [] } neSpine (app u n) with neSpine u ... | record{ head = x; spine = s} = record{ head = x; spine = snoc s n} -- Application: there is no closed normal term of the Peirce type. noClosedNeutral : ε ⊢ᵤ a → ⊥ noClosedNeutral (var ()) noClosedNeutral (app u _) = noClosedNeutral u private A = ` "A" B = ` "B" -- Lemma: we can't derive A ⇒ B from just (A ⇒ B) ⇒ A. lemma : ε ∙ (A ⇒ B) ⇒ A ⊢ₙ A ⇒ B → ⊥ lemma (ne u) with neSpine u ... | record { head = zero ; spine = t ∷ () } lemma (abs (ne u)) with neSpine u ... | record { head = suc zero ; spine = n ∷ () } -- The Peirce formula has no normal derivation. noPeirce : ε ⊢ₙ ((A ⇒ B) ⇒ A) ⇒ A → ⊥ noPeirce (ne u) = noClosedNeutral u noPeirce (abs (ne u)) with neSpine u ... | record { head = zero ; spine = n ∷ [] } = lemma 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 NbEStructure where World = Context open Term.Weakening public using () renaming (Wk to _≤_; idW to refl; compWW to trans) open NormalForms public using (_⊢ᵤ_) renaming (weakᵤ to monₐ) _⊩ₐ_ : World → BaseTy → Set Γ ⊩ₐ X = Γ ⊢ᵤ ` X module NbE where s : Kripke s = record{ NbEStructure } 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)