Files
lean-pl-tutorials/tutorial-02-semantics/13-hm-declarative.org
Hermes Agent 6e2914b06e migrate all tutorials from Markdown to Org mode
Converted with pandoc 3.7 (markdown → org), all 17 files:
- README, references, 15 tutorial units
- Internal file links updated from .md to .org
- Source code blocks (#+begin_src lean) preserved
- Tables, math notation, links intact

For the Emacs workflow.
2026-05-28 20:15:38 +02:00

133 lines
5.0 KiB
Org Mode
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
* Unit 13 --- Hindley-Milner Declarative Typing Rules
:PROPERTIES:
:CUSTOM_ID: unit-13-hindley-milner-declarative-typing-rules
:END:
*Tutorial 2: PL Semantics in Lean* · [[../README.org][← Back to README]]
** Goals
:PROPERTIES:
:CUSTOM_ID: goals
:END:
- Extend STLC with *let-polymorphism*
- Encode HM types with *type schemes* =∀α. τ=
- Formalize the 6 declarative typing rules (Var, App, Abs, Let, Inst, Gen)
- Understand the distinction between monomorphic (=τ=) and polymorphic (=σ = ∀α.τ=) types
** Sources
:PROPERTIES:
:CUSTOM_ID: sources
:END:
- [[https://en.wikipedia.org/wiki/Hindley%E2%80%93Milner_type_system][Wikipedia: Hindley-Milner type system]]
- [[https://www.jeffvaughan.net/docs/hmproof.pdf][Vaughan 2008]] (declarative rules, §2)
- [[https://github.com/sdemos/type-inference][sdemos/type-inference]] (Lean formalization)
** Background
:PROPERTIES:
:CUSTOM_ID: background
:END:
HM adds *let-polymorphism* to STLC:
#+begin_example
let x = e₁ in e₂
#+end_example
In STLC, =x= would have a single monomorphic type. In HM, =x= gets a type
/scheme/ --- e.g., =∀α. αα= for the identity function --- and each use of =x=
instantiates the scheme with fresh type variables.
The declarative rules:
#+begin_example
Var: Γ, x:σ ⊢ x : σ
App: Γ ⊢ e₁ : τ₁ → τ₂ Γ ⊢ e₂ : τ₁ ⇛ Γ ⊢ e₁ e₂ : τ₂
Abs: Γ, x:τ₁ ⊢ e : τ₂ ⇛ Γ ⊢ λx.e : τ₁ → τ₂
Let: Γ ⊢ e₁ : σ Γ, x:σ ⊢ e₂ : τ ⇛ Γ ⊢ let x = e₁ in e₂ : τ
Inst: Γ ⊢ e : ∀α.τ ⇛ Γ ⊢ e : τ[α := τ']
Gen: Γ ⊢ e : τ ∧ α ∉ ftv(Γ) ⇛ Γ ⊢ e : ∀α.τ
#+end_example
** Exercises
:PROPERTIES:
:CUSTOM_ID: exercises
:END:
#+begin_src lean
-- 13.1 — Types and type schemes
inductive MonoType where
| tvar (id : Nat) -- type variable α
| fn (τ₁ τ₂ : MonoType) -- τ₁ → τ₂
deriving Repr, DecidableEq
notation:40 τ₁ "" τ₂ => MonoType.fn τ₁ τ₂
-- A type scheme: ∀α₁...αₙ. τ (quantified over a set of type variables)
structure TypeScheme where
vars : List Nat -- bound type variables
body : MonoType
deriving Repr
-- 13.2 — HM expression syntax (extend STLC with `let`)
inductive HMExpr where
| var (i : Nat) -- de Bruijn index
| lam (body : HMExpr) -- λ. e
| app (f a : HMExpr) -- f a
| lett (e₁ e₂ : HMExpr) -- let x = e₁ in e₂
deriving Repr
-- 13.3 — Environments
-- Γ maps de Bruijn indices to type schemes
def HMEnv := List TypeScheme
-- 13.4 — Declarative typing rules
inductive HMTyping : HMEnv HMExpr TypeScheme Prop where
-- Var: look up variable in the environment
| var (Γ : HMEnv) (i : Nat) (h : lookup Γ i = some σ) : HMTyping Γ (HMExpr.var i) σ
-- Abs: λx. e — note: x gets a monomorphic type in the lambda body
| abs (Γ : HMEnv) (e : HMExpr) (τ₁ τ₂ : MonoType)
(h : HMTyping ({vars := [], body := τ₁} :: Γ) e {vars := [], body := τ₂}) :
HMTyping Γ (HMExpr.lam e) {vars := [], body := τ₁ τ₂}
-- App: e₁ e₂
| app (Γ : HMEnv) (e₁ e₂ : HMExpr) (σ τ₁ τ₂ : MonoType) (σ' : TypeScheme)
(h₁ : HMTyping Γ e₁ σ') (h₂ : HMTyping Γ e₂ {vars := [], body := τ₁})
(h_eq : σ' = {vars := [], body := τ₁ τ₂}) :
HMTyping Γ (HMExpr.app e₁ e₂) {vars := [], body := τ₂}
-- Let: e₁ is generalized and e₂ can use the polymorphic type
| lett (Γ : HMEnv) (e₁ e₂ : HMExpr) (σ σ' : TypeScheme) (τ : MonoType)
(h₁ : HMTyping Γ e₁ σ) (h₂ : HMTyping (σ :: Γ) e₂ σ') :
HMTyping Γ (HMExpr.lett e₁ e₂) σ'
-- Gen: generalize over free type variables not in the environment
| gen (Γ : HMEnv) (e : HMExpr) (σ : TypeScheme) (αs : List Nat)
(h : HMTyping Γ e σ) (hfresh : α, α αs α ftv_env Γ) :
HMTyping Γ e {σ with vars := αs ++ σ.vars}
-- Inst: instantiate a type scheme
| inst (Γ : HMEnv) (e : HMExpr) (σ : TypeScheme) (τ : MonoType) (subst : Nat MonoType)
(h : HMTyping Γ e σ) (hinst : apply_subst subst σ = τ) :
HMTyping Γ e {vars := [], body := τ}
-- 13.5 — Exercise: the identity function is polymorphic
def id_type : TypeScheme := {vars := [0], body := MonoType.tvar 0 MonoType.tvar 0}
theorem id_polymorphic : HMTyping [] (HMExpr.lam (HMExpr.var 0)) id_type :=
by
sorry
-- 13.6 — Exercise: let x = λy. y in x x (instantiation of polymorphic identity)
-- This term should be well-typed — the identity is used at two different types
def self_app_id : HMExpr :=
HMExpr.lett (HMExpr.lam (HMExpr.var 0)) (HMExpr.app (HMExpr.var 0) (HMExpr.var 0))
theorem self_app_id_typed (τ : MonoType) :
HMTyping [] self_app_id {vars := [], body := τ τ} :=
by
sorry
#+end_src
--------------
← [[file:12-type-safety.org][Previous: Unit 12]] · Next: [[file:14-algorithm-w.org][Unit 14 --- Algorithm W]]