/-! # Simply typed Ξ»-calculus β€” the Lean on the slides Self-contained: needs only Lean 4 (v4.34.0-rc1 or later), no library. How to run * Install Lean via elan: https://lean-lang.org/install * Open this file in VS Code with the "Lean 4" extension. Results of `#eval`/`#check` appear in the Infoview; errors are underlined. * Or, in a terminal: `lean simply-typed-lambda-calculus.lean` `-- Slide N` names the page of the deck the code appears on. Blocks marked "support" are definitions the slides use but do not show. Definition numbers are Nederpelt & Geuvers, *Type Theory and Formal Proof*, ch. 2. -/ set_option linter.unusedVariables false /-! ## Slide 6 β€” 𝕋, as an `inductive` (Def. 2.2.1) -/ inductive Ty where | base : String β†’ Ty | arrow : Ty β†’ Ty β†’ Ty deriving DecidableEq infixr:25 " β‡’ " => Ty.arrow -- infixr: right-associative #check (Ty.base "Ξ±" β‡’ Ty.base "Ξ²" β‡’ Ty.base "Ξ±") -- Ξ± β‡’ (Ξ² β‡’ Ξ±) /-! ### Support: print a type as the book writes it, and two type variables -/ protected def Ty.repr : Ty β†’ Nat β†’ Std.Format | .base a, _ => a | .arrow a b, p => let f := f!"{a.repr 26} β‡’ {b.repr 25}" if p > 25 then Std.Format.paren f else f instance : Repr Ty := ⟨Ty.repr⟩ def Ξ± : Ty := .base "Ξ±" def Ξ² : Ty := .base "Ξ²" /-! ## Slide 10 β€” The size argument, as a tactic proof (Ex. 2.2.6(3)) -/ def size : Ty β†’ Nat | .base _ => 1 | .arrow a b => size a + size b + 1 theorem arrow_ne (Οƒ Ο„ : Ty) : (Οƒ β‡’ Ο„) β‰  Οƒ := by intro h have := congrArg size h simp [size] at this omega /-! ## Slide 11 β€” Ill-typed programs are rejected -/ def double (n : Nat) : Nat := 2 * n /-- error: Application type mismatch: The argument "one" has type String but is expected to have type Nat in the application double "one" -/ #guard_msgs in -- records the error Lean reports def bad : Nat := double "one" /-! ## Slide 13 β€” Lean is Church-typed underneath (Β§2.3) -/ set_option pp.funBinderTypes true -- 1. Annotated binder: reaches the checker as written. #check fun (x : Nat) => x -- fun (x : Nat) => x : Nat β†’ Nat -- 2. Bare binder, expected type Nat β†’ Nat: Nat is written on it. #check (fun x => x : Nat β†’ Nat) -- fun (x : Nat) => x : Nat β†’ Nat -- 3. Bare binder, no expected type: the placeholder ?m.1 stays, -- and a definition may not contain one. #check fun x => x -- fun (x : ?m.1) => x : ?m.1 β†’ ?m.1 -- rejected: "Failed to infer type of binder `x`" #guard_msgs (drop error) in def idc := fun x => x /-! ## Slide 14 β€” Pre-typed terms Ξ›_T (Def. 2.4.1) -/ inductive TTerm where | var : String β†’ TTerm | app : TTerm β†’ TTerm β†’ TTerm | lam : String β†’ Ty β†’ TTerm β†’ TTerm deriving Repr, DecidableEq -- Example 2.4.6's term, Ξ»y:Ξ±β‡’Ξ². Ξ»z:Ξ±. y z, as a value: def ex246 : TTerm := .lam "y" (Ξ± β‡’ Ξ²) (.lam "z" Ξ± (.app (.var "y") (.var "z"))) /-! ## Slide 16 β€” A context is a list (Def. 2.4.2) -/ abbrev Ctx := List (String Γ— Ty) #check ([("x", Ξ±), ("y", Ξ± β‡’ Ξ²)] : Ctx) /-! ## Slide 21 β€” The three rules, as an `inductive` (Def. 2.4.5) -/ inductive Derives : Ctx β†’ TTerm β†’ Ty β†’ Prop where | var {Ξ“ x Οƒ} : Ξ“.lookup x = some Οƒ β†’ -- (var) Derives Ξ“ (.var x) Οƒ | app {Ξ“ M N Οƒ Ο„} : Derives Ξ“ M (Οƒ β‡’ Ο„) β†’ Derives Ξ“ N Οƒ β†’ -- (appl) Derives Ξ“ (.app M N) Ο„ | abs {Ξ“ x M Οƒ Ο„} : Derives ((x, Οƒ) :: Ξ“) M Ο„ β†’ -- (abst) Derives Ξ“ (.lam x Οƒ M) (Οƒ β‡’ Ο„) notation:40 Ξ“ " ⊒ " M " : " Οƒ => Derives Ξ“ M Οƒ /-! ## Slide 24 β€” The same derivation, in flag format Not on the slide: the flag derivation of Example 2.4.6 as a tactic script, at Ξ± := Nat, Ξ² := String. Each `intro` opens a flag, `exact` is the line under the flags, and leaving the block closes them, innermost first. -/ def ex246Lean : (Nat β†’ String) β†’ Nat β†’ String := by intro y -- flag (a): y : Nat β†’ String ⊒ Nat β†’ String intro z -- flag (b): z : Nat ⊒ String exact y z -- line (3): y z : String; (1), (2) are the uses of y, z -- end of block: (4) closes flag (b), (5) closes flag (a) #print ex246Lean -- fun y z => y z, the term of line (5) -- With no arrow left in the goal there is no flag to open: a third `intro` -- fails with "There are no additional binders … in the goal to introduce". #guard_msgs (drop error) in example : (Nat β†’ String) β†’ Nat β†’ String := by intro y z w exact y z /-! ## Slide 25 β€” That derivation, as a term (Example 2.4.6) -/ -- line (5), then (4), (3), (1) and (2) of the flag derivation example : [] ⊒ ex246 : ((Ξ± β‡’ Ξ²) β‡’ Ξ± β‡’ Ξ²) := .abs (.abs (.app (.var rfl) (.var rfl))) /-! ## Slide 29 β€” The rules, read backwards, are an algorithm (Rem. 2.4.7) -/ def infer (Ξ“ : Ctx) : TTerm β†’ Option Ty | .var x => Ξ“.lookup x | .app M N => match infer Ξ“ M, infer Ξ“ N with | some (Οƒ β‡’ Ο„), some Οƒ' => if Οƒ = Οƒ' then some Ο„ else none | _, _ => none | .lam x Οƒ M => (infer ((x, Οƒ) :: Ξ“) M).map (Οƒ β‡’ Β·) #eval infer [] ex246 -- some ((Ξ± β‡’ Ξ²) β‡’ Ξ± β‡’ Ξ²) #eval infer [] (.app ex246 ex246) -- none /-! ## Slide 32 β€” Soundness, in Lean -/ theorem infer_sound : βˆ€ {Ξ“ M Οƒ}, infer Ξ“ M = some Οƒ β†’ Ξ“ ⊒ M : Οƒ | Ξ“, .var x, Οƒ, h => .var h -- h is the side condition | Ξ“, .lam x ρ P, Οƒ, h => by -- Οƒ is (ρ β‡’ Ο„), from some Ο„ obtain βŸ¨Ο„, hP, rfl⟩ := Option.map_eq_some_iff.mp h exact .abs (infer_sound hP) | Ξ“, .app P Q, Οƒ, h => by unfold infer at h split at h <;> try (cases h) -- only the first arm answers rename_i ρ Ο„ _ hP hQ split at h <;> cases h -- and only if the test passed subst_vars exact .app (infer_sound hP) (infer_sound hQ) /-! ## Slide 34 β€” Completeness, in Lean -/ theorem infer_complete {Ξ“ M Οƒ} (d : Ξ“ ⊒ M : Οƒ) : infer Ξ“ M = some Οƒ := by induction d with | var h => simpa [infer] using h -- the side condition | app _ _ ihM ihN => simp [infer, ihM, ihN] -- the input types agree | abs _ ih => simp [infer, ih] -- prefix ρ β‡’ /-! ## Slide 35 β€” Decidability, as a tactic (Thm. 2.10.10(2)) -/ -- run `infer`; the two lemmas turn its answer into a verdict instance (Ξ“ : Ctx) (M : TTerm) (Οƒ : Ty) : Decidable (Ξ“ ⊒ M : Οƒ) := if h : infer Ξ“ M = some Οƒ then .isTrue (infer_sound h) else .isFalse fun d => h (infer_complete d) example : [] ⊒ ex246 : ((Ξ± β‡’ Ξ²) β‡’ Ξ± β‡’ Ξ²) := by decide example : Β¬ ([] ⊒ .app ex246 ex246 : Ξ±) := by decide /-! ## Slide 38 β€” Ξ»β†’ is too weak -/ def id' {Ξ± : Type} (x : Ξ±) : Ξ± := x -- one identity, for every type #check (List : Type β†’ Type) -- a type built from a type #check (Fin 3 : Type) -- a type mentioning a term