4.6 KiB
4.6 KiB
Unit 13 — Hindley-Milner Declarative Typing Rules
Tutorial 2: PL Semantics in Lean · ← Back to README
Goals
- 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
- Wikipedia: Hindley-Milner type system
- Vaughan 2008 (declarative rules, §2)
- sdemos/type-inference (Lean formalization)
Background
HM adds let-polymorphism to STLC:
let x = e₁ in e₂
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:
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 : ∀α.τ
Exercises
-- 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
← Previous: Unit 12 · Next: Unit 14 — Algorithm W