-- https://plfa.github.io/Lambda/ import Plfl.Init namespace Lambda open String def Sym : Type := String deriving BEq, DecidableEq, Repr -- https://plfa.github.io/Lambda/#syntax-of-terms inductive Term where | var : Sym → Term | lam : Sym → Term → Term | ap : Term → Term → Term | zero : Term | succ : Term → Term | case : Term → Term → Sym → Term → Term | mu : Sym → Term → Term deriving BEq, DecidableEq, Repr namespace Term notation:50 "ƛ " v " : " d => lam v d notation:50 " μ " v " : " d => mu v d notation:max "𝟘? " e " [zero: " o " |succ " n " : " i " ] " => case e o n i infixr:min " $ " => ap infixl:70 " □ " => ap prefix:80 "ι " => succ prefix:90 "` " => var notation "𝟘" => zero example : Term := `"foo" example : Term := 𝟘? `"bar" [zero: 𝟘 |succ "n" : ι 𝟘] @[simp] def ofNat | 0 => zero | n + 1 => succ <| ofNat n instance : Coe ℕ Term where coe := ofNat instance : OfNat Term n where ofNat := ofNat n example : Term := 1 example : Term := 42 abbrev add : Term := μ "+" : ƛ "m" : ƛ "n" : 𝟘? `"m" [zero: `"n" |succ "m": ι (`"+" □ `"m" □ `"n")] -- https://plfa.github.io/Lambda/#exercise-mul-recommended abbrev mul : Term := μ "*" : ƛ "m" : ƛ "n" : 𝟘? `"m" [zero: 𝟘 |succ "m": add □ `"n" $ `"*" □ `"m" □ `"n"] -- Church encoding... abbrev succC : Term := ƛ "n" : ι `"n" abbrev oneC : Term := ƛ "s" : ƛ "z" : `"s" $ `"z" abbrev twoC : Term := ƛ "s" : ƛ "z" : `"s" $ `"s" $ `"z" abbrev addC : Term := ƛ "m" : ƛ "n" : ƛ "s" : ƛ "z" : `"m" □ `"s" $ `"n" □ `"s" □ `"z" -- https://plfa.github.io/Lambda/#exercise-mul%E1%B6%9C-practice abbrev mulC : Term := ƛ "m" : ƛ "n" : ƛ "s" : ƛ "z" : `"m" □ (`"n" □ `"s") □ `"z" end Term -- https://plfa.github.io/Lambda/#values inductive Value : Term → Type where | lam : Value (ƛ v : d) | zero: Value 𝟘 | succ: Value n → Value (ι n) deriving BEq, DecidableEq, Repr namespace Value notation "V𝟘" => zero def ofNat : (n : ℕ) → Value (Term.ofNat n) | 0 => V𝟘 | n + 1 => succ <| ofNat n -- instance : CoeDep ℕ n (Value ↑n) where coe := ofNat n -- instance : OfNat (Value (Term.ofNat n)) n where ofNat := ofNat n end Value -- https://plfa.github.io/Lambda/#substitution namespace Term /-- `x.subst y v` substitutes term `v` for all free occurrences of variable `y` in term `x`. -/ def subst : Term → Sym → Term → Term | ` x, y, v => if x = y then v else ` x | ƛ x : n, y, v => if x = y then ƛ x : n else ƛ x : n.subst y v | ap l m, y, v => l.subst y v $ m.subst y v | 𝟘, _, _ => 𝟘 | ι n, y, v => ι (n.subst y v) | 𝟘? l [zero: m |succ x: n], y, v => if x = y then 𝟘? l.subst y v [zero: m.subst y v |succ x: n] else 𝟘? l.subst y v [zero: m.subst y v |succ x: n.subst y v] | μ x : n, y, v => if x = y then μ x : n else μ x : n.subst y v notation:90 x " [ " y " := " v " ] " => subst x y v -- https://plfa.github.io/Lambda/#examples example : (ƛ "z" : `"s" □ `"s" □ `"z")["s" := succC] = (ƛ "z" : succC □ succC □ `"z") := rfl example : (succC □ succC □ `"z")["z" := 𝟘] = succC □ succC □ 𝟘 := rfl example : (ƛ "x" : `"y")["y" := 𝟘] = (ƛ "x" : 𝟘) := rfl example : (ƛ "x" : `"x")["x" := 𝟘] = (ƛ "x" : `"x") := rfl example : (ƛ "y" : `"y")["x" := 𝟘] = (ƛ "y" : `"y") := rfl -- https://plfa.github.io/Lambda/#quiz example : (ƛ "y" : `"x" $ ƛ "x" : `"x")["x" := 𝟘] = (ƛ "y" : 𝟘 $ ƛ "x" : `"x") := rfl -- https://plfa.github.io/Lambda/#reduction /-- `Reduce t t'` says that `t` reduces to `t'`. -/ inductive Reduce : Term → Term → Type where | lamβ : Value v → Reduce ((ƛ x : n) □ v) (n[x := v]) | apξ₁ : Reduce l l' → Reduce (l □ m) (l' □ m) | apξ₂ : Value v → Reduce m m' → Reduce (v □ m) (v □ m') | zeroβ : Reduce (𝟘? 𝟘 [zero: m |succ x : n]) m | succβ : Value v → Reduce (𝟘? ι v [zero: m |succ x : n]) (n[x := v]) | succξ : Reduce m m' → Reduce (ι m) (ι m') | caseξ : Reduce l l' → Reduce (𝟘? l [zero: m |succ x : n]) (𝟘? l' [zero: m |succ x : n]) | muβ : Reduce (μ x : m) (m[x := μ x : m]) deriving Repr infix:40 " —→ " => Reduce end Term namespace Term.Reduce -- https://plfa.github.io/Lambda/#quiz-1 example : (ƛ "x" : `"x") □ (ƛ "x" : `"x") —→ (ƛ "x" : `"x") := by apply lamβ; exact Value.lam example : (ƛ "x" : `"x") □ (ƛ "x" : `"x") □ (ƛ "x" : `"x") —→ (ƛ "x" : `"x") □ (ƛ "x" : `"x") := by apply apξ₁; apply lamβ; exact Value.lam example : twoC □ succC □ 𝟘 —→ (ƛ "z" : succC $ succC $ `"z") □ 𝟘 := by unfold twoC; apply apξ₁; apply lamβ; exact Value.lam -- https://plfa.github.io/Lambda/#reflexive-and-transitive-closure /-- A reflexive and transitive closure, defined as a sequence of zero or more steps of the underlying relation `—→`. -/ inductive Clos : Term → Term → Type where | nil : Clos m m | cons : (l —→ m) → Clos m n → Clos l n deriving Repr infix:20 " —↠ " => Clos namespace Clos def length : (m —↠ n) → Nat | nil => 0 | cons _ cdr => 1 + cdr.length abbrev one (car : m —→ n) : (m —↠ n) := cons car nil instance : Coe (m —→ n) (m —↠ n) where coe := one def trans : (l —↠ m) → (m —↠ n) → (l —↠ n) | nil, c => c | cons h c, c' => cons h <| c.trans c' instance : Trans Clos Clos Clos where trans := trans instance : Trans Reduce Clos Clos where trans := cons instance : Trans Reduce Reduce Clos where trans c c' := cons c <| cons c' nil def transOne : (l —↠ m) → (m —→ n) → (l —↠ n) | nil, c => c | cons h c, c' => cons h <| c.trans c' instance : Trans Clos Reduce Clos where trans := transOne end Clos inductive Clos' : Term → Term → Type where | refl : Clos' m m | step : (m —→ n) → Clos' m n | trans : Clos' l m → Clos' m n → Clos' l n infix:20 " —↠' " => Clos' def Clos.toClos' : (m —↠ n) → (m —↠' n) := by intro | nil => exact Clos'.refl | cons h h' => exact Clos'.trans (Clos'.step h) h'.toClos' def Clos'.toClos : (m —↠' n) → (m —↠ n) := by intro | refl => exact Clos.nil | step h => exact ↑h | trans h h' => apply Clos.trans <;> (apply toClos; assumption) -- https://plfa.github.io/Lambda/#exercise-practice lemma Clos.toClos'_left_inv : ∀ {x : m —↠ n}, x.toClos'.toClos = x := by intro | nil => rfl | cons car cdr => simp_all only [Clos'.toClos, trans, cons.injEq, heq_eq_eq, true_and] exact toClos'_left_inv (x := cdr) lemma Clos.toClos'_inj : @Function.Injective (m —↠ n) (m —↠' n) Clos.toClos' := by unfold Function.Injective intro a b h apply_fun Clos'.toClos at h rwa [←toClos'_left_inv (x := a), ←toClos'_left_inv (x := b)] instance Clos.embedsInClos' : (m —↠ n) ↪ (m —↠' n) where toFun := toClos' inj' := toClos'_inj end Term.Reduce -- https://plfa.github.io/Lambda/#confluence section confluence open Term.Reduce Term.Reduce.Clos -- `Σ` is used instead of `∃` because it's a `Type` that exists, not a `Prop`. def Diamond : Type := ∀ ⦃l m n⦄, (l —→ m) → (l —→ n) → (Σ p, (m —↠ p) × (n —↠ p)) def Confluence : Type := ∀ ⦃l m n⦄, (l —↠ m) → (l —↠ n) → (Σ p, (m —↠ p) × (n —↠ p)) def Deterministic : Prop := ∀ ⦃l m n⦄, (l —→ m) → (l —→ n) → (m = n) def Deterministic.toDiamond : Deterministic → Diamond := by unfold Deterministic Diamond; intro h l m n lm ln have heq := h lm ln; simp_all only exists n; exact ⟨nil, nil⟩ def Deterministic.toConfluence : Deterministic → Confluence | h, l, m, n, lm, ln => by match lm, ln with | nil, nil => exists n; exact ⟨ln, ln⟩ | nil, c@(cons _ _) => exists n; exact ⟨c, nil⟩ | c@(cons _ _), nil => exists m; exact ⟨nil, c⟩ | cons car cdr, cons car' cdr' => have := h car car'; subst this exact toConfluence h cdr cdr' end confluence -- https://plfa.github.io/Lambda/#examples-1 section examples open Term Term.Reduce Term.Reduce.Clos example : twoC □ succC □ 𝟘 —↠ 2 := calc twoC □ succC □ 𝟘 _ —→ (ƛ "z" : succC $ succC $ `"z") □ 𝟘 := by apply apξ₁; apply lamβ; exact Value.lam _ —→ (succC $ succC $ 𝟘) := by apply lamβ; exact Value.zero _ —→ succC □ 1 := by apply apξ₂; apply Value.lam; apply lamβ; exact Value.zero _ —→ 2 := by apply lamβ; exact Value.ofNat 1 -- https://plfa.github.io/Lambda/#exercise-plus-example-practice example : add □ 1 □ 1 —↠ 2 := calc add □ 1 □ 1 _ —→ (ƛ "m" : ƛ "n" : 𝟘? `"m" [zero: `"n" |succ "m": ι (add □ `"m" □ `"n")]) □ 1 □ 1 := by apply apξ₁; apply apξ₁; apply muβ _ —↠ (ƛ "n" : 𝟘? 1 [zero: `"n" |succ "m": ι (add □ `"m" □ `"n")]) □ 1 := .one <| by apply apξ₁; apply lamβ; exact Value.ofNat 1 _ —→ 𝟘? 1 [zero: 1 |succ "m": ι (add □ `"m" □ 1)] := lamβ <| Value.ofNat 1 _ —→ ι (add □ 𝟘 □ 1) := succβ Value.zero _ —→ ι ((ƛ "m" : ƛ "n" : 𝟘? `"m" [zero: `"n" |succ "m": ι (add □ `"m" □ `"n")]) □ 𝟘 □ 1) := by apply succξ; apply apξ₁; apply apξ₁; apply muβ _ —→ ι ((ƛ "n" : 𝟘? 𝟘 [zero: `"n" |succ "m": ι (add □ `"m" □ `"n")]) □ 1) := by apply succξ; apply apξ₁; apply lamβ; exact V𝟘 _ —→ ι (𝟘? 𝟘 [zero: 1 |succ "m": ι (add □ `"m" □ 1)]) := by apply succξ; apply lamβ; exact Value.ofNat 1 _ —→ 2 := succξ zeroβ end examples -- https://plfa.github.io/Lambda/#syntax-of-types inductive Ty where | nat | fn : Ty → Ty → Ty deriving BEq, DecidableEq, Repr namespace Ty notation "ℕt" => nat infixr:70 " =⇒ " => fn example : Ty := (ℕt =⇒ ℕt) =⇒ ℕt theorem t_to_t'_ne_t (t t' : Ty) : (t =⇒ t') ≠ t := by by_contra h; match t with | nat => trivial | fn ta tb => injection h; have := t_to_t'_ne_t ta tb; contradiction end Ty -- https://plfa.github.io/Lambda/#contexts def Context : Type := List (Sym × Ty) namespace Context open Term def nil : Context := [] def extend : Context → Sym → Ty → Context | c, s, ts => ⟨s, ts⟩ :: c notation "∅" => nil -- The goal is to make `_‚_⦂_` work like an `infixl`. -- https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html#From-Precedence-to-Binding-Power -- `‚` is not a comma! See: notation:50 c " ‚ " s:51 " ⦂ " t:51 => extend c s t example {Γ : Context} {s : Sym} {ts : Ty} : Context := Γ‚ s ⦂ ts -- https://plfa.github.io/Lambda/#lookup-judgment /-- A lookup judgement. `Lookup c s ts` means that `s` is of type `ts` by _looking up_ the context `c`. -/ @[aesop safe [constructors, cases]] inductive Lookup : Context → Sym → Ty → Type where | z : Lookup (Γ‚ x ⦂ t) x t | s : x ≠ y → Lookup Γ x t → Lookup (Γ‚ y ⦂ u) x t deriving DecidableEq notation:40 c " ∋ " s " ⦂ " t:51 => Lookup c s t example : ∅‚ "x" ⦂ ℕt =⇒ ℕt‚ "y" ⦂ ℕt‚ "z" ⦂ ℕt ∋ "x" ⦂ ℕt =⇒ ℕt := open Lookup in by apply s _; apply s _; apply z; repeat trivial -- https://plfa.github.io/Lambda/#lookup-is-functional theorem Lookup.functional : Γ ∋ x ⦂ t → Γ ∋ x ⦂ t' → t = t' := by intro | z, z => rfl | z, s _ e => trivial | s _ e, z => trivial | s _ e, s _ e' => exact functional e e' -- https://plfa.github.io/Lambda/#typing-judgment /-- A general typing judgement. `IsTy c t tt` means that `t` can be inferred to be of type `tt` in the context `c`. -/ inductive IsTy : Context → Term → Ty → Type where | tyVar : Γ ∋ x ⦂ t → IsTy Γ (` x) t | tyLam : IsTy (Γ‚ x ⦂ t) n u → IsTy Γ (ƛ x : n) (t =⇒ u) | tyAp : IsTy Γ l (t =⇒ u) → IsTy Γ x t → IsTy Γ (l □ x) u | tyZero : IsTy Γ 𝟘 ℕt | tySucc : IsTy Γ n ℕt → IsTy Γ (ι n) ℕt | tyCase : IsTy Γ l ℕt → IsTy Γ m t → IsTy (Γ‚ x ⦂ ℕt) n t → IsTy Γ (𝟘? l [zero: m |succ x: n]) t | tyMu : IsTy (Γ‚ x ⦂ t) m t → IsTy Γ (μ x : m) t deriving DecidableEq notation:40 c " ⊢ " t " ⦂ " tt:51 => IsTy c t tt /-- `NoTy c t` means that `t` cannot be inferred to be any type in the context `c`. -/ abbrev NoTy (c : Context) (t : Term) : Prop := ∀ {tt}, IsEmpty (c ⊢ t ⦂ tt) infix:40 " ⊬ " => NoTy -- https://github.com/arthurpaulino/lean4-metaprogramming-book/blob/d6a227a63c55bf13d49d443f47c54c7a500ea27b/md/main/tactics.md#tactics-by-macro-expansion /-- `lookup_var` validates the type of a variable by looking it up in the current context. This tactic fails when the lookup fails. -/ syntax "lookup_var" : tactic macro_rules | `(tactic| lookup_var) => `(tactic| apply IsTy.tyVar; repeat (first | apply Lookup.s (by trivial) | exact Lookup.z)) -- Inform `trivial` of our new tactic. macro_rules | `(tactic| trivial) => `(tactic| lookup_var) open IsTy -- https://plfa.github.io/Lambda/#quiz-2 lemma twice_ty : Γ ⊢ (ƛ "s" : `"s" $ `"s" $ 𝟘) ⦂ ((ℕt =⇒ ℕt) =⇒ ℕt) := by apply tyLam; apply tyAp · trivial · apply tyAp · trivial · exact tyZero theorem two_ty : Γ ⊢ (ƛ "s" : `"s" $ `"s" $ 𝟘) □ succC ⦂ ℕt := by apply tyAp twice_ty · apply tyLam; apply tySucc; trivial -- https://plfa.github.io/Lambda/#derivation abbrev NatC (t : Ty) : Ty := (t =⇒ t) =⇒ t =⇒ t theorem twoC_ty : Γ ⊢ twoC ⦂ NatC t := by apply tyLam; apply tyLam; apply tyAp · trivial · apply tyAp <;> trivial def addTy : Γ ⊢ add ⦂ ℕt =⇒ ℕt =⇒ ℕt := by repeat apply_rules [tyAp, tyMu, tyLam, tyCase, tySucc, tyZero] <;> trivial theorem addC_ty : Γ ⊢ addC ⦂ NatC t =⇒ NatC t =⇒ NatC t := by repeat apply tyLam <;> try trivial · repeat apply tyAp <;> try trivial -- https://plfa.github.io/Lambda/#exercise-mul-recommended-1 def mulTy : Γ ⊢ mul ⦂ ℕt =⇒ ℕt =⇒ ℕt := by repeat apply_rules [tyAp, tyMu, tyLam, tyCase, tySucc, tyZero] <;> trivial -- https://plfa.github.io/Lambda/#exercise-mul%E1%B6%9C-practice-1 theorem mulC_ty : Γ ⊢ mulC ⦂ NatC t =⇒ NatC t =⇒ NatC t := by repeat apply tyLam <;> try trivial · repeat apply tyAp <;> try trivial end Context section examples open Term Context Lookup IsTy -- https://plfa.github.io/Lambda/#non-examples example : ∅ ⊬ 𝟘 □ 1 := by by_contra h; simp_all only [not_isEmpty_iff] cases h.some; contradiction abbrev illLam := ƛ "x" : `"x" □ `"x" lemma nty_illLam : ∅ ⊬ illLam := by by_contra h; simp_all only [not_isEmpty_iff] let tyLam (tyAp (tyVar hx) (tyVar hx')) := h.some have := Lookup.functional hx hx'; simp_all only [Ty.t_to_t'_ne_t] -- https://plfa.github.io/Lambda/#quiz-3 example : ∅‚ "y" ⦂ ℕt =⇒ ℕt‚ "x" ⦂ ℕt ⊢ `"y" □ `"x" ⦂ ℕt := by apply tyAp <;> trivial example : ∅‚ "y" ⦂ ℕt =⇒ ℕt‚ "x" ⦂ ℕt ⊬ `"x" □ `"y" := by by_contra h; simp_all only [not_isEmpty_iff] let ⟨ht⟩ := h cases ht; rename_i hy hx · cases hx; rename_i ty hx · cases hx; contradiction example : ∅‚ "y" ⦂ ℕt =⇒ ℕt ⊢ ƛ "x" : `"y" □ `"x" ⦂ ℕt =⇒ ℕt := by apply tyLam; apply tyAp <;> trivial example : ∅‚ "x" ⦂ t ⊬ `"x" □ `"x" := by by_contra h; simp_all only [not_isEmpty_iff] let ⟨ht⟩ := h cases ht; rename_i hx · cases hx; rename_i hx · cases hx <;> contradiction example : ∅‚ "x" ⦂ ℕt =⇒ ℕt‚ "y" ⦂ ℕt =⇒ ℕt ⊢ ƛ "z" : (`"x" $ `"y" $ `"z") ⦂ ℕt =⇒ ℕt := by apply tyLam; apply tyAp <;> try trivial · apply tyAp <;> trivial end examples