Lambda Calculus in Lean

5. Subject Reduction and Normal Forms🔗

import LeanLambda.SimplyTyped.Typingnamespace SimplyTypedopen Untypednamespace DB

5.1. Subject Reduction and Normal Forms🔗

This chapter studies the genuinely type-dependent facts about reduction and normal forms.

  1. Preservation: reduction does not change the type of a term.

  2. Representation: typing restricts the possible shapes of normal terms.

Weakening and substitution are supporting lemmas needed for preservation. The exhaustive and exclusive relationship between reduction and normality was already established for untyped terms and does not need to be repeated here.

5.1.1. Structural Lemmas🔗

5.1.1.1. Weakening🔗

Inserting a type into the bound context shifts precisely the indices at or above the insertion point. This is the typing form of weakening.

\frac{\Delta_1,\Delta_2;\Gamma \vdash e : A} {\Delta_1,C,\Delta_2;\Gamma \vdash \uparrow_{|\Delta_1|}e : A}

theorem typing_shift (ht : left ++ right ; Γ ⊢ᴺᵇ t : A) : left ++ C :: right ; Γ ⊢ᴺᵇ DB.shiftAbove left.length t : A := left:BoundContextright:BoundContextΓ:FreeContextt:DBA:TyC:Tyht:left ++ right ; Γ ⊢ᴺᵇ t : Aleft ++ C :: right ; Γ ⊢ᴺᵇ DB.shiftAbove (List.length left) t : A Γ:FreeContextC:Tyindex✝:left:BoundContextright:BoundContextA:Tyht:left ++ right ; Γ ⊢ᴺᵇ DB.bound index✝ : Aleft ++ C :: right ; Γ ⊢ᴺᵇ DB.shiftAbove (List.length left) (DB.bound index✝) : AΓ:FreeContextC:Tyx✝:Stringleft:BoundContextright:BoundContextA:Tyht:left ++ right ; Γ ⊢ᴺᵇ DB.free x✝ : Aleft ++ C :: right ; Γ ⊢ᴺᵇ DB.shiftAbove (List.length left) (DB.free x✝) : AΓ:FreeContextC:Tybody✝:DBbody_ih✝: {left right : BoundContext} {A : Ty}, (left ++ right ; Γ ⊢ᴺᵇ body✝ : A) left ++ C :: right ; Γ ⊢ᴺᵇ DB.shiftAbove (List.length left) body✝ : Aleft:BoundContextright:BoundContextA:Tyht:left ++ right ; Γ ⊢ᴺᵇ body✝.lam : Aleft ++ C :: right ; Γ ⊢ᴺᵇ DB.shiftAbove (List.length left) body✝.lam : AΓ:FreeContextC:Tyfn✝:DBarg✝:DBfn_ih✝: {left right : BoundContext} {A : Ty}, (left ++ right ; Γ ⊢ᴺᵇ fn✝ : A) left ++ C :: right ; Γ ⊢ᴺᵇ DB.shiftAbove (List.length left) fn✝ : Aarg_ih✝: {left right : BoundContext} {A : Ty}, (left ++ right ; Γ ⊢ᴺᵇ arg✝ : A) left ++ C :: right ; Γ ⊢ᴺᵇ DB.shiftAbove (List.length left) arg✝ : Aleft:BoundContextright:BoundContextA:Tyht:left ++ right ; Γ ⊢ᴺᵇ fn✝.app arg✝ : Aleft ++ C :: right ; Γ ⊢ᴺᵇ DB.shiftAbove (List.length left) (fn✝.app arg✝) : A Γ:FreeContextC:Tyindex✝:left:BoundContextright:BoundContextA:Tyht:left ++ right ; Γ ⊢ᴺᵇ DB.bound index✝ : Aleft ++ C :: right ; Γ ⊢ᴺᵇ DB.shiftAbove (List.length left) (DB.bound index✝) : AΓ:FreeContextC:Tyx✝:Stringleft:BoundContextright:BoundContextA:Tyht:left ++ right ; Γ ⊢ᴺᵇ DB.free x✝ : Aleft ++ C :: right ; Γ ⊢ᴺᵇ DB.shiftAbove (List.length left) (DB.free x✝) : AΓ:FreeContextC:Tybody✝:DBbody_ih✝: {left right : BoundContext} {A : Ty}, (left ++ right ; Γ ⊢ᴺᵇ body✝ : A) left ++ C :: right ; Γ ⊢ᴺᵇ DB.shiftAbove (List.length left) body✝ : Aleft:BoundContextright:BoundContextA:Tyht:left ++ right ; Γ ⊢ᴺᵇ body✝.lam : Aleft ++ C :: right ; Γ ⊢ᴺᵇ DB.shiftAbove (List.length left) body✝.lam : AΓ:FreeContextC:Tyfn✝:DBarg✝:DBfn_ih✝: {left right : BoundContext} {A : Ty}, (left ++ right ; Γ ⊢ᴺᵇ fn✝ : A) left ++ C :: right ; Γ ⊢ᴺᵇ DB.shiftAbove (List.length left) fn✝ : Aarg_ih✝: {left right : BoundContext} {A : Ty}, (left ++ right ; Γ ⊢ᴺᵇ arg✝ : A) left ++ C :: right ; Γ ⊢ᴺᵇ DB.shiftAbove (List.length left) arg✝ : Aleft:BoundContextright:BoundContextA:Tyht:left ++ right ; Γ ⊢ᴺᵇ fn✝.app arg✝ : Aleft ++ C :: right ; Γ ⊢ᴺᵇ DB.shiftAbove (List.length left) (fn✝.app arg✝) : A Γ:FreeContextC:Tyfn✝:DBarg✝:DBfn_ih✝: {left right : BoundContext} {A : Ty}, (left ++ right ; Γ ⊢ᴺᵇ fn✝ : A) left ++ C :: right ; Γ ⊢ᴺᵇ DB.shiftAbove (List.length left) fn✝ : Aarg_ih✝: {left right : BoundContext} {A : Ty}, (left ++ right ; Γ ⊢ᴺᵇ arg✝ : A) left ++ C :: right ; Γ ⊢ᴺᵇ DB.shiftAbove (List.length left) arg✝ : Aleft:BoundContextright:BoundContextA:TyA✝:Tya✝¹:left ++ right ; Γ ⊢ᴺᵇ arg✝ : A✝a✝:left ++ right ; Γ ⊢ᴺᵇ fn✝ : A✝ Aleft ++ C :: right ; Γ ⊢ᴺᵇ DB.shiftAbove (List.length left) (fn✝.app arg✝) : A Γ:FreeContextC:Tyindex✝:left:BoundContextright:BoundContextA:Tya✝:(left ++ right)[index✝]? = some Aleft ++ C :: right ; Γ ⊢ᴺᵇ DB.shiftAbove (List.length left) (DB.bound index✝) : AΓ:FreeContextC:Tyx✝:Stringleft:BoundContextright:BoundContextA:Tya✝:List.lookup x✝ Γ = some Aleft ++ C :: right ; Γ ⊢ᴺᵇ DB.shiftAbove (List.length left) (DB.free x✝) : AΓ:FreeContextC:Tybody✝:DBbody_ih✝: {left right : BoundContext} {A : Ty}, (left ++ right ; Γ ⊢ᴺᵇ body✝ : A) left ++ C :: right ; Γ ⊢ᴺᵇ DB.shiftAbove (List.length left) body✝ : Aleft:BoundContextright:BoundContextA✝:TyB✝:Tya✝:A✝ :: (left ++ right) ; Γ ⊢ᴺᵇ body✝ : B✝left ++ C :: right ; Γ ⊢ᴺᵇ DB.shiftAbove (List.length left) body✝.lam : A✝ B✝Γ:FreeContextC:Tyfn✝:DBarg✝:DBfn_ih✝: {left right : BoundContext} {A : Ty}, (left ++ right ; Γ ⊢ᴺᵇ fn✝ : A) left ++ C :: right ; Γ ⊢ᴺᵇ DB.shiftAbove (List.length left) fn✝ : Aarg_ih✝: {left right : BoundContext} {A : Ty}, (left ++ right ; Γ ⊢ᴺᵇ arg✝ : A) left ++ C :: right ; Γ ⊢ᴺᵇ DB.shiftAbove (List.length left) arg✝ : Aleft:BoundContextright:BoundContextA:TyA✝:Tya✝¹:left ++ right ; Γ ⊢ᴺᵇ arg✝ : A✝a✝:left ++ right ; Γ ⊢ᴺᵇ fn✝ : A✝ Aleft ++ C :: right ; Γ ⊢ᴺᵇ DB.shiftAbove (List.length left) (fn✝.app arg✝) : A All goals completed! 🐙@[grind .] theorem typing_shift_head (ht : Δ ; Γ ⊢ᴺᵇ t : A) : C :: Δ ; Γ ⊢ᴺᵇ DB.shiftAbove 0 t : A := Δ:BoundContextΓ:FreeContextt:DBA:TyC:Tyht:Δ ; Γ ⊢ᴺᵇ t : AC :: Δ ; Γ ⊢ᴺᵇ DB.shiftAbove 0 t : A All goals completed! 🐙

5.1.1.2. Substitution🔗

Removing one entry from the bound context corresponds to substituting a term of that entry's type. The three variable cases are the substituted variable itself, an earlier index, and a later index. Under a lambda, the argument is shifted and remains well-typed by weakening.

\frac{\Delta_1,\Delta_2;\Gamma \vdash s : C \qquad \Delta_1,C,\Delta_2;\Gamma \vdash e : A} {\Delta_1,\Delta_2;\Gamma \vdash [s/|\Delta_1|]e : A}

@[grind .] theorem typing_substitution (harg : left ++ right ; Γ ⊢ᴺᵇ arg : C) (hbody : left ++ C :: right ; Γ ⊢ᴺᵇ body : A) : left ++ right ; Γ ⊢ᴺᵇ DB.subst left.length arg body : A := left:BoundContextright:BoundContextΓ:FreeContextarg:DBC:Tybody:DBA:Tyharg:left ++ right ; Γ ⊢ᴺᵇ arg : Chbody:left ++ C :: right ; Γ ⊢ᴺᵇ body : Aleft ++ right ; Γ ⊢ᴺᵇ DB.subst (List.length left) arg body : A induction body generalizing left right arg C A with Γ:FreeContextbody:DBih: {left right : BoundContext} {arg : DB} {C A : Ty}, (left ++ right ; Γ ⊢ᴺᵇ arg : C) (left ++ C :: right ; Γ ⊢ᴺᵇ body : A) left ++ right ; Γ ⊢ᴺᵇ DB.subst (List.length left) arg body : Aleft:BoundContextright:BoundContextarg:DBC:TyA:Tyharg:left ++ right ; Γ ⊢ᴺᵇ arg : Chbody:left ++ C :: right ; Γ ⊢ᴺᵇ body.lam : Aleft ++ right ; Γ ⊢ᴺᵇ DB.subst (List.length left) arg body.lam : A cases hbody with Γ:FreeContextbody:DBih: {left right : BoundContext} {arg : DB} {C A : Ty}, (left ++ right ; Γ ⊢ᴺᵇ arg : C) (left ++ C :: right ; Γ ⊢ᴺᵇ body : A) left ++ right ; Γ ⊢ᴺᵇ DB.subst (List.length left) arg body : Aleft:BoundContextright:BoundContextarg:DBC:Tyharg:left ++ right ; Γ ⊢ᴺᵇ arg : CD:TyB✝:Tya✝:D :: (left ++ C :: right) ; Γ ⊢ᴺᵇ body : B✝left ++ right ; Γ ⊢ᴺᵇ DB.subst (List.length left) arg body.lam : D B✝ All goals completed! 🐙 Γ:FreeContextfn✝:DBarg✝:DBfn_ih✝: {left right : BoundContext} {arg : DB} {C A : Ty}, (left ++ right ; Γ ⊢ᴺᵇ arg : C) (left ++ C :: right ; Γ ⊢ᴺᵇ fn✝ : A) left ++ right ; Γ ⊢ᴺᵇ DB.subst (List.length left) arg fn✝ : Aarg_ih✝: {left right : BoundContext} {arg : DB} {C A : Ty}, (left ++ right ; Γ ⊢ᴺᵇ arg : C) (left ++ C :: right ; Γ ⊢ᴺᵇ arg✝ : A) left ++ right ; Γ ⊢ᴺᵇ DB.subst (List.length left) arg arg✝ : Aleft:BoundContextright:BoundContextarg:DBC:TyA:Tyharg:left ++ right ; Γ ⊢ᴺᵇ arg : Chbody:left ++ C :: right ; Γ ⊢ᴺᵇ fn✝.app arg✝ : Aleft ++ right ; Γ ⊢ᴺᵇ DB.subst (List.length left) arg (fn✝.app arg✝) : AΓ:FreeContextx✝:Stringleft:BoundContextright:BoundContextarg:DBC:TyA:Tyharg:left ++ right ; Γ ⊢ᴺᵇ arg : Chbody:left ++ C :: right ; Γ ⊢ᴺᵇ DB.free x✝ : Aleft ++ right ; Γ ⊢ᴺᵇ DB.subst (List.length left) arg (DB.free x✝) : AΓ:FreeContextindex✝:left:BoundContextright:BoundContextarg:DBC:TyA:Tyharg:left ++ right ; Γ ⊢ᴺᵇ arg : Chbody:left ++ C :: right ; Γ ⊢ᴺᵇ DB.bound index✝ : Aleft ++ right ; Γ ⊢ᴺᵇ DB.subst (List.length left) arg (DB.bound index✝) : A Γ:FreeContextfn✝:DBarg✝:DBfn_ih✝: {left right : BoundContext} {arg : DB} {C A : Ty}, (left ++ right ; Γ ⊢ᴺᵇ arg : C) (left ++ C :: right ; Γ ⊢ᴺᵇ fn✝ : A) left ++ right ; Γ ⊢ᴺᵇ DB.subst (List.length left) arg fn✝ : Aarg_ih✝: {left right : BoundContext} {arg : DB} {C A : Ty}, (left ++ right ; Γ ⊢ᴺᵇ arg : C) (left ++ C :: right ; Γ ⊢ᴺᵇ arg✝ : A) left ++ right ; Γ ⊢ᴺᵇ DB.subst (List.length left) arg arg✝ : Aleft:BoundContextright:BoundContextarg:DBC:TyA:Tyharg:left ++ right ; Γ ⊢ᴺᵇ arg : CA✝:Tya✝¹:left ++ C :: right ; Γ ⊢ᴺᵇ arg✝ : A✝a✝:left ++ C :: right ; Γ ⊢ᴺᵇ fn✝ : A✝ Aleft ++ right ; Γ ⊢ᴺᵇ DB.subst (List.length left) arg (fn✝.app arg✝) : A all_goals All goals completed! 🐙@[grind .] theorem typing_substitution_head (harg : Δ ; Γ ⊢ᴺᵇ arg : C) (hbody : C :: Δ ; Γ ⊢ᴺᵇ body : A) : Δ ; Γ ⊢ᴺᵇ DB.subst 0 arg body : A := Δ:BoundContextΓ:FreeContextarg:DBC:Tybody:DBA:Tyharg:Δ ; Γ ⊢ᴺᵇ arg : Chbody:C :: Δ ; Γ ⊢ᴺᵇ body : AΔ ; Γ ⊢ᴺᵇ DB.subst 0 arg body : A All goals completed! 🐙

5.1.2. Preservation🔗

This is the first main result. Its beta case is exactly the substitution theorem; the remaining reduction rules follow from the induction hypotheses.

\frac{\Delta;\Gamma \vdash e : A \qquad e \to_\beta e'} {\Delta;\Gamma \vdash e' : A}

theorem preservation (hstep : t →βᵇ t') : (Δ ; Γ ⊢ᴺᵇ t : A) (Δ ; Γ ⊢ᴺᵇ t' : A) := t:DBt':DBΔ:BoundContextΓ:FreeContextA:Tyhstep:t →βᵇ t'(Δ ; Γ ⊢ᴺᵇ t : A) Δ ; Γ ⊢ᴺᵇ t' : A t:DBt':DBΔ:BoundContextΓ:FreeContextA:Tyhstep:t →βᵇ t'ht:Δ ; Γ ⊢ᴺᵇ t : AΔ ; Γ ⊢ᴺᵇ t' : A; t:DBt':DBbody✝:DBarg✝:DBΔ:BoundContextΓ:FreeContextA:Tyht:Δ ; Γ ⊢ᴺᵇ body✝.lam.app arg✝ : AΔ ; Γ ⊢ᴺᵇ DB.subst 0 arg✝ body✝ : At:DBt':DBf✝:DBf'✝:DBa✝¹:DBa✝:f✝ →βᵇ f'✝a_ih✝: {Δ : BoundContext} {Γ : FreeContext} {A : Ty}, (Δ ; Γ ⊢ᴺᵇ f✝ : A) Δ ; Γ ⊢ᴺᵇ f'✝ : AΔ:BoundContextΓ:FreeContextA:Tyht:Δ ; Γ ⊢ᴺᵇ f✝.app a✝¹ : AΔ ; Γ ⊢ᴺᵇ f'✝.app a✝¹ : At:DBt':DBa✝¹:DBa'✝:DBf✝:DBa✝:a✝¹ →βᵇ a'✝a_ih✝: {Δ : BoundContext} {Γ : FreeContext} {A : Ty}, (Δ ; Γ ⊢ᴺᵇ a✝¹ : A) Δ ; Γ ⊢ᴺᵇ a'✝ : AΔ:BoundContextΓ:FreeContextA:Tyht:Δ ; Γ ⊢ᴺᵇ f✝.app a✝¹ : AΔ ; Γ ⊢ᴺᵇ f✝.app a'✝ : At:DBt':DBbody✝:DBbody'✝:DBa✝:body✝ →βᵇ body'✝a_ih✝: {Δ : BoundContext} {Γ : FreeContext} {A : Ty}, (Δ ; Γ ⊢ᴺᵇ body✝ : A) Δ ; Γ ⊢ᴺᵇ body'✝ : AΔ:BoundContextΓ:FreeContextA:Tyht:Δ ; Γ ⊢ᴺᵇ body✝.lam : AΔ ; Γ ⊢ᴺᵇ body'✝.lam : A t:DBt':DBbody✝:DBarg✝:DBΔ:BoundContextΓ:FreeContextA:Tyht:Δ ; Γ ⊢ᴺᵇ body✝.lam.app arg✝ : AΔ ; Γ ⊢ᴺᵇ DB.subst 0 arg✝ body✝ : At:DBt':DBf✝:DBf'✝:DBa✝¹:DBa✝:f✝ →βᵇ f'✝a_ih✝: {Δ : BoundContext} {Γ : FreeContext} {A : Ty}, (Δ ; Γ ⊢ᴺᵇ f✝ : A) Δ ; Γ ⊢ᴺᵇ f'✝ : AΔ:BoundContextΓ:FreeContextA:Tyht:Δ ; Γ ⊢ᴺᵇ f✝.app a✝¹ : AΔ ; Γ ⊢ᴺᵇ f'✝.app a✝¹ : At:DBt':DBa✝¹:DBa'✝:DBf✝:DBa✝:a✝¹ →βᵇ a'✝a_ih✝: {Δ : BoundContext} {Γ : FreeContext} {A : Ty}, (Δ ; Γ ⊢ᴺᵇ a✝¹ : A) Δ ; Γ ⊢ᴺᵇ a'✝ : AΔ:BoundContextΓ:FreeContextA:Tyht:Δ ; Γ ⊢ᴺᵇ f✝.app a✝¹ : AΔ ; Γ ⊢ᴺᵇ f✝.app a'✝ : At:DBt':DBbody✝:DBbody'✝:DBa✝:body✝ →βᵇ body'✝a_ih✝: {Δ : BoundContext} {Γ : FreeContext} {A : Ty}, (Δ ; Γ ⊢ᴺᵇ body✝ : A) Δ ; Γ ⊢ᴺᵇ body'✝ : AΔ:BoundContextΓ:FreeContextA:Tyht:Δ ; Γ ⊢ᴺᵇ body✝.lam : AΔ ; Γ ⊢ᴺᵇ body'✝.lam : A All goals completed! 🐙@[grind .] theorem preservation_star (hsteps : t →βᵇ* t') : (Δ ; Γ ⊢ᴺᵇ t : A) (Δ ; Γ ⊢ᴺᵇ t' : A) := t:DBt':DBΔ:BoundContextΓ:FreeContextA:Tyhsteps:t →βᵇ* t'(Δ ; Γ ⊢ᴺᵇ t : A) Δ ; Γ ⊢ᴺᵇ t' : A t:DBt':DBΔ:BoundContextΓ:FreeContextA:Ty(Δ ; Γ ⊢ᴺᵇ t : A) Δ ; Γ ⊢ᴺᵇ t : At:DBt':DBΔ:BoundContextΓ:FreeContextA:Tyb✝:DBc✝:DBa✝¹:Relation.ReflTransGen DB.BetaStep t b✝a✝:b✝ →βᵇ c✝a_ih✝:(Δ ; Γ ⊢ᴺᵇ t : A) Δ ; Γ ⊢ᴺᵇ b✝ : A(Δ ; Γ ⊢ᴺᵇ t : A) Δ ; Γ ⊢ᴺᵇ c✝ : A t:DBt':DBΔ:BoundContextΓ:FreeContextA:Ty(Δ ; Γ ⊢ᴺᵇ t : A) Δ ; Γ ⊢ᴺᵇ t : At:DBt':DBΔ:BoundContextΓ:FreeContextA:Tyb✝:DBc✝:DBa✝¹:Relation.ReflTransGen DB.BetaStep t b✝a✝:b✝ →βᵇ c✝a_ih✝:(Δ ; Γ ⊢ᴺᵇ t : A) Δ ; Γ ⊢ᴺᵇ b✝ : A(Δ ; Γ ⊢ᴺᵇ t : A) Δ ; Γ ⊢ᴺᵇ c✝ : A All goals completed! 🐙

The same statement for any finite reduction sequence follows immediately by induction on the sequence.

5.1.3. Closed Normal Terms🔗

The key observation is that a typed neutral term must ultimately be headed by a variable from its context. If every type in that context is atomic, the head cannot be applied to an argument, so the whole neutral term is that variable. This is the neutrality lemma used in representation proofs.

theorem neutrality {atoms : List String} (hneutral : DB.Neutral t) (ht : atoms.map Ty.var ; [] ⊢ᴺᵇ t : B) : n X, t = .bound n atoms[n]? = some X B = .var X := t:DBB:Tyatoms:List Stringhneutral:t.Neutralht:List.map Ty.var atoms ; [] ⊢ᴺᵇ t : B n X, t = DB.bound n atoms[n]? = some X B = Ty.var X cases hneutral with B:Tyatoms:List Stringfn✝:DBarg✝:DBhfn:fn✝.Neutrala✝:arg✝.Normalht:List.map Ty.var atoms ; [] ⊢ᴺᵇ fn✝.app arg✝ : B n X, fn✝.app arg✝ = DB.bound n atoms[n]? = some X B = Ty.var X B:Tyatoms:List Stringfn✝:DBarg✝:DBhfn:fn✝.Neutrala✝²:arg✝.NormalA✝:Tya✝¹:List.map Ty.var atoms ; [] ⊢ᴺᵇ arg✝ : A✝a✝:List.map Ty.var atoms ; [] ⊢ᴺᵇ fn✝ : A✝ B n X, fn✝.app arg✝ = DB.bound n atoms[n]? = some X B = Ty.var X All goals completed! 🐙 B:Tyatoms:List Stringx✝:Stringht:List.map Ty.var atoms ; [] ⊢ᴺᵇ DB.free x✝ : B n X, DB.free x✝ = DB.bound n atoms[n]? = some X B = Ty.var XB:Tyatoms:List Stringn✝:ht:List.map Ty.var atoms ; [] ⊢ᴺᵇ DB.bound n✝ : B n X, DB.bound n✝ = DB.bound n atoms[n]? = some X B = Ty.var X B:Tyatoms:List Stringx✝:Stringa✝:List.lookup x✝ [] = some B n X, DB.free x✝ = DB.bound n atoms[n]? = some X B = Ty.var X All goals completed! 🐙

In the empty context there is no variable that could head a neutral term. Consequently every closed, well-typed normal term is an abstraction.

Closed normal forms. If a closed, well-typed term is normal, then it is an abstraction.

@[grind .] theorem Neutral.not_typed_empty (hneutral : DB.Neutral t) : ¬ ([] ⊢ᵇ t : A) := t:DBA:Tyhneutral:t.Neutral¬[] ⊢ᵇ t : A All goals completed! 🐙theorem closed_normal_is_abstraction (ht : [] ⊢ᵇ t : A) (hnormal : DB.Normal t) : body, t = DB.lam body := t:DBA:Tyht:[] ⊢ᵇ t : Ahnormal:t.Normal body, t = body.lam A:Tybody✝:DBa✝:body✝.Normalht:[] ⊢ᵇ body✝.lam : A body, body✝.lam = body.lamt:DBA:Tyht:[] ⊢ᵇ t : Aa✝:t.Neutral body, t = body.lam A:Tybody✝:DBa✝:body✝.Normalht:[] ⊢ᵇ body✝.lam : A body, body✝.lam = body.lamt:DBA:Tyht:[] ⊢ᵇ t : Aa✝:t.Neutral body, t = body.lam All goals completed! 🐙

5.1.4. Representation of Booleans🔗

The Church Booleans share the type X → X → X: true returns its first argument and false returns its second. Conversely, these are the only closed normal terms of that type. Typing and normality first force two lambdas. The remaining body is a neutral term of type X in a context containing two variables of type X; neutrality says it must be one of those variables.

\cdot \vdash e : X \to X \to X \quad e\ \mathsf{normal} \quad\Longrightarrow\quad e = \lambda.\lambda.1 \;\text{or}\; e = \lambda.\lambda.0

theorem boolean_representation (ht : [] ⊢ᵇ t : Ty.var X Ty.var X Ty.var X) (hnormal : DB.Normal t) : t = DB[λ λ 1] t = DB[λ λ 0] := t:DBX:Stringht:[] ⊢ᵇ t : Ty.var X Ty.var X Ty.var Xhnormal:t.Normalt = (DB.bound 1).lam.lam t = (DB.bound 0).lam.lam cases hnormal with t:DBX:Stringht:[] ⊢ᵇ t : Ty.var X Ty.var X Ty.var Xhneutral:t.Neutralt = (DB.bound 1).lam.lam t = (DB.bound 0).lam.lam All goals completed! 🐙 X:Stringbody✝:DBhbody:body✝.Normalht:[] ⊢ᵇ body✝.lam : Ty.var X Ty.var X Ty.var Xbody✝.lam = (DB.bound 1).lam.lam body✝.lam = (DB.bound 0).lam.lam X:Stringbody✝:DBhbody:body✝.Normala✝:[Ty.var X] ; [] ⊢ᴺᵇ body✝ : Ty.var X Ty.var Xbody✝.lam = (DB.bound 1).lam.lam body✝.lam = (DB.bound 0).lam.lam cases hbody with X:Stringbody✝:DBa✝:[Ty.var X] ; [] ⊢ᴺᵇ body✝ : Ty.var X Ty.var Xhneutral:body✝.Neutralbody✝.lam = (DB.bound 1).lam.lam body✝.lam = (DB.bound 0).lam.lam All goals completed! 🐙 X:Stringbody✝:DBhresult:body✝.Normala✝:[Ty.var X] ; [] ⊢ᴺᵇ body✝.lam : Ty.var X Ty.var Xbody✝.lam.lam = (DB.bound 1).lam.lam body✝.lam.lam = (DB.bound 0).lam.lam X:Stringbody✝:DBhresult:body✝.Normala✝:[Ty.var X, Ty.var X] ; [] ⊢ᴺᵇ body✝ : Ty.var Xbody✝.lam.lam = (DB.bound 1).lam.lam body✝.lam.lam = (DB.bound 0).lam.lam cases hresult with X:Stringbody✝:DBa✝¹:body✝.Normala✝:[Ty.var X, Ty.var X] ; [] ⊢ᴺᵇ body✝.lam : Ty.var Xbody✝.lam.lam.lam = (DB.bound 1).lam.lam body✝.lam.lam.lam = (DB.bound 0).lam.lam All goals completed! 🐙 X:Stringbody✝:DBa✝:[Ty.var X, Ty.var X] ; [] ⊢ᴺᵇ body✝ : Ty.var Xhneutral:body✝.Neutralbody✝.lam.lam = (DB.bound 1).lam.lam body✝.lam.lam = (DB.bound 0).lam.lam All goals completed! 🐙end DB

5.1.5. Named Presentation🔗

We now state the main results using the named terms that appear in mathematical writing. Their proofs use the de Bruijn development, but these named versions are the statements we will normally cite and apply later in the course.

namespace Term

5.1.5.1. Alpha-Equivalence🔗

Changing binder names does not affect typing, provided the corresponding binder types remain the same.

@[grind .] theorem typing_respects_alpha : (Δ.types = Δ'.types) (e =α[Δ.names, Δ'.names] e') (Δ ; Γ ⊢ᴺ e : A) (Δ' ; Γ ⊢ᴺ e' : A) := e:Terme':TermΔ:BinderContextΓ:FreeContextA:TyΔ':BinderContextΔ.types = Δ'.types (e =α[Δ.names, Δ'.names] e') (Δ ; Γ ⊢ᴺ e : A) Δ' ; Γ ⊢ᴺ e' : A All goals completed! 🐙

5.1.5.2. Preservation🔗

Preservation. If a well-typed named term takes a beta step, its type is unchanged.

@[grind .] theorem preservation_with (hstep : e →β[Δ.names, Δ.names] e') : (Δ ; Γ ⊢ᴺ e : A) (Δ ; Γ ⊢ᴺ e' : A) := e:Terme':TermΔ:BinderContextΓ:FreeContextA:Tyhstep:e →β[Δ.names, Δ.names] e'(Δ ; Γ ⊢ᴺ e : A) Δ ; Γ ⊢ᴺ e' : A All goals completed! 🐙@[grind .] theorem preservation_star_with (hsteps : e →β*[Δ.names, Δ.names] e') : (Δ ; Γ ⊢ᴺ e : A) (Δ ; Γ ⊢ᴺ e' : A) := e:Terme':TermΔ:BinderContextΓ:FreeContextA:Tyhsteps:e →β*[Δ.names, Δ.names] e'(Δ ; Γ ⊢ᴺ e : A) Δ ; Γ ⊢ᴺ e' : A All goals completed! 🐙theorem preservation (hstep : e →β e') : (Γ e : A) (Γ e' : A) := e:Terme':TermΓ:FreeContextA:Tyhstep:e →β e'(Γ e : A) Γ e' : A All goals completed! 🐙theorem preservation_star (hsteps : e →β* e') : (Γ e : A) (Γ e' : A) := e:Terme':TermΓ:FreeContextA:Tyhsteps:e →β* e'(Γ e : A) Γ e' : A All goals completed! 🐙

5.1.5.3. Closed Normal Terms🔗

Closed normal forms. A closed, well-typed named term that is normal must be a lambda abstraction.

theorem closed_normal_is_abstraction (ht : [] e : A) (hnormal : Term.Normal e) : x body, e = .lam x body := e:TermA:Tyht:[] e : Ahnormal:e.Normal x body, e = Term.lam x body A:Tyx✝:Stringht:[] Term.var x✝ : Ahnormal:(Term.var x✝).Normal x body, Term.var x✝ = Term.lam x bodyA:Tyx✝:Stringbody✝:Termht:[] Term.lam x✝ body✝ : Ahnormal:(Term.lam x✝ body✝).Normal x body, Term.lam x✝ body✝ = Term.lam x bodyA:Tyfn✝:Termarg✝:Termht:[] fn✝.app arg✝ : Ahnormal:(fn✝.app arg✝).Normal x body, fn✝.app arg✝ = Term.lam x body A:Tyx✝:Stringht:[] Term.var x✝ : Ahnormal:(Term.var x✝).Normal x body, Term.var x✝ = Term.lam x bodyA:Tyx✝:Stringbody✝:Termht:[] Term.lam x✝ body✝ : Ahnormal:(Term.lam x✝ body✝).Normal x body, Term.lam x✝ body✝ = Term.lam x bodyA:Tyfn✝:Termarg✝:Termht:[] fn✝.app arg✝ : Ahnormal:(fn✝.app arg✝).Normal x body, fn✝.app arg✝ = Term.lam x body All goals completed! 🐙

5.1.5.4. Representation of Booleans🔗

Boolean representation. Up to alpha-equivalence, the only closed normal named terms of type X → X → X are the two Church Booleans λx. λy. x and λx. λy. y. This is the first representation theorem of the course: a type and normality together determine exactly which values it can represent.

theorem boolean_representation (ht : [] e : Ty.var X Ty.var X Ty.var X) (hnormal : Term.Normal e) : e Term[λx. λy. x] e Term[λx. λy. y] := e:TermX:Stringht:[] e : Ty.var X Ty.var X Ty.var Xhnormal:e.Normale Term.lam "x" (Term.lam "y" (Term.var "x")) e Term.lam "x" (Term.lam "y" (Term.var "y")) All goals completed! 🐙end Termend SimplyTyped