Curry-Howard correspondence relating proofs and programs, propositions and types
Scope: Propositions as types, proofs as programs, logical connectives as type constructors Lines: ~380 Last Updated: 2025-10-25
Activate this skill when:
Curry-Howard Isomorphism: Deep connection between:
| Logic | Type Theory | Programming | |-------|-------------|-------------| | Proposition P | Type P | Type P | | Proof of P | Term of type P | Value of type P | | P → Q | Function type P → Q | Function from P to Q | | P ∧ Q | Product type P × Q | Pair (p, q) | | P ∨ Q | Sum type P + Q | Either left(p) or right(q) | | ⊤ (true) | Unit type 1 | () | | ⊥ (false) | Empty type 0 | No values | | ¬P | P → ⊥ | Function from P to absurdity | | ∀x. P(x) | Π(x:A). P(x) | Dependent function | | ∃x. P(x) | Σ(x:A). P(x) | Dependent pair |
from dataclasses import dataclass
from typing import Union
# Propositions as types
@dataclass
class Implies:
"""P → Q"""
premise: 'Prop'
conclusion: 'Prop'
def __repr__(self):
return f"({self.premise} → {self.conclusion})"
@dataclass
class And:
"""P ∧ Q"""
left: 'Prop'
right: 'Prop'
def __repr__(self):
return f"({self.left} ∧ {self.right})"
@dataclass
class Or:
"""P ∨ Q"""
left: 'Prop'
right: 'Prop'
def __repr__(self):
return f"({self.left} ∨ {self.right})"
@dataclass
class Not:
"""¬P ≡ P → ⊥"""
prop: 'Prop'
def __repr__(self):
return f"¬{self.prop}"
@dataclass
class True_:
"""⊤"""
def __repr__(self):
return "⊤"
@dataclass
class False_:
"""⊥"""
def __repr__(self):
return "⊥"
Prop = Union[Implies, And, Or, Not, True_, False_]
# Proofs as programs
@dataclass
class Lambda:
"""λx. M - proof of P → Q"""
var: str
body: 'Proof'
@dataclass
class Pair:
"""(M, N) - proof of P ∧ Q"""
fst: 'Proof'
snd: 'Proof'
@dataclass
class Inl:
"""inl(M) - proof of P ∨ Q (left)"""
proof: 'Proof'
@dataclass
class Inr:
"""inr(N) - proof of P ∨ Q (right)"""
proof: 'Proof'
@dataclass
class Unit:
"""() - proof of ⊤"""
pass
Proof = Union[Lambda, Pair, Inl, Inr, Unit]
print("Proposition P → Q corresponds to function type P → Q")
print("Proof of P → Q is a function taking proof of P to proof of Q")
Implication (→):
# P → Q corresponds to function type P → Q
# Proof: λx:P. M : Q (function taking P to Q)
# Example: Prove A → A (identity)
# Proof: λx. x
# In Lean 4:
"""
theorem id (A : Prop) : A → A := fun x => x
-- Or with tactics:
theorem id' (A : Prop) : A → A := by
intro x
exact x
"""
def prove_implication_id():
"""A → A: identity function"""
return Lambda('x', Variable('x'))
print("Proof of A → A is the identity function: λx. x")
Conjunction (∧):
# P ∧ Q corresponds to product type P × Q
# Proof: (M, N) where M : P, N : Q
# Example: Prove (A ∧ B) → (B ∧ A) (commutativity)
# Proof: λp. (snd p, fst p)
# In Lean 4:
"""
theorem and_comm (A B : Prop) : A ∧ B → B ∧ A := by
intro ⟨a, b⟩
exact ⟨b, a⟩
-- Equivalent:
theorem and_comm' (A B : Prop) : A ∧ B → B ∧ A :=
fun ⟨a, b⟩ => ⟨b, a⟩
"""
def prove_and_comm():
"""(A ∧ B) → (B ∧ A)"""
return Lambda('p', Pair(Project2(Variable('p')), Project1(Variable('p'))))
print("Proof of (A ∧ B) → (B ∧ A) swaps pair components")
Disjunction (∨):
# P ∨ Q corresponds to sum type P + Q
# Proof: inl(M) : P ∨ Q or inr(N) : P ∨ Q
# Example: Prove A → (A ∨ B)
# Proof: λx. inl(x)
# In Lean 4:
"""
theorem or_intro_left (A B : Prop) : A → A ∨ B := by
intro a
left
exact a
-- Equivalent:
theorem or_intro_left' (A B : Prop) : A → A ∨ B :=
fun a => Or.inl a
"""
def prove_or_intro_left():
"""A → (A ∨ B)"""
return Lambda('x', Inl(Variable('x')))
print("Proof of A → (A ∨ B) injects A into left side")
Negation (¬):
# ¬P ≡ P → ⊥
# Proof of ¬P is a function from P to absurdity
# Example: Prove ¬(A ∧ ¬A) (non-contradiction)
# Proof: λp. (snd p) (fst p)
# In Lean 4:
"""
theorem not_and_not (A : Prop) : ¬(A ∧ ¬A) := by
intro ⟨a, na⟩
exact na a
-- Equivalent:
theorem not_and_not' (A : Prop) : ¬(A ∧ ¬A) :=
fun ⟨a, na⟩ => na a
"""
def prove_non_contradiction():
"""¬(A ∧ ¬A)"""
# (A ∧ ¬A) → ⊥
# λp. (snd p) (fst p) [where snd p : A → ⊥, fst p : A]
return Lambda('p', Apply(Project2(Variable('p')), Project1(Variable('p'))))
print("Proof of ¬(A ∧ ¬A): apply ¬A to A to get ⊥")
Extracting programs from proofs:
# Lean 4 example: Extracting program from constructive proof
"""
-- Prove: ∃(x:Nat). x > 0
theorem exists_positive : ∃(x:Nat), x > 0 := by
use 1
norm_num
-- Extract witness:
#eval (exists_positive.choose : Nat) -- 1
-- Extracting a sorting function from proof of sorting correctness:
def sort (xs : List Nat) : {ys : List Nat // Sorted ys ∧ Permutation xs ys} := ...
-- Type ensures:
-- 1. Result is sorted
-- 2. Result is permutation of input
-- Program AND proof in one!
"""
print("Proofs in constructive logic contain computational content")
print("Can extract programs from proofs")
Law of Excluded Middle (LEM): P ∨ ¬P
# LEM is NOT provable in intuitionistic logic
# Cannot construct P ∨ ¬P without knowing which one holds
# In classical logic (Lean with Classical axiom):
"""
open Classical
theorem lem (P : Prop) : P ∨ ¬P := em P
-- Non-constructive proof by contradiction:
theorem sqrt_2_irrational : Irrational (√2) := by
by_contra h
-- Assume √2 is rational, derive contradiction
...
"""
# In intuitionistic logic:
"""
-- Can only prove double negation of LEM:
theorem not_not_lem (P : Prop) : ¬¬(P ∨ ¬P) := by
intro h
apply h
right
intro p
apply h
left
exact p
"""
print("Classical logic: P ∨ ¬P always holds")
print("Intuitionistic logic: Need explicit construction of P or ¬P")
Universal quantification (∀):
# ∀(x:A). P(x) corresponds to Π(x:A). P(x)
# Proof: function from x to proof of P(x)
# Example: Prove ∀(n:Nat). n = n
# Proof: λn. refl
# In Lean 4:
"""
theorem forall_eq (n : Nat) : n = n := rfl
-- Explicitly as ∀:
theorem forall_eq' : ∀(n : Nat), n = n := fun n => rfl
"""
print("Proof of ∀(n:Nat). P(n) is a function λn. proof of P(n)")
Existential quantification (∃):
# ∃(x:A). P(x) corresponds to Σ(x:A). P(x)
# Proof: pair (witness, proof that witness satisfies P)
# Example: Prove ∃(n:Nat). n > 5
# Proof: (6, proof that 6 > 5)
# In Lean 4:
"""
theorem exists_gt_5 : ∃(n : Nat), n > 5 := by
use 6
norm_num
-- Equivalent:
theorem exists_gt_5' : ∃(n : Nat), n > 5 :=
⟨6, by norm_num⟩
"""
print("Proof of ∃(n:Nat). P(n) is a pair: (witness, proof P(witness))")
-- Lean 4
theorem modus_ponens (P Q : Prop) : (P → Q) → P → Q :=
fun f p => f p
-- Program: function application!
-- Lean 4
theorem curry (A B C : Prop) : ((A ∧ B) → C) → (A → B → C) :=
fun f a b => f ⟨a, b⟩
theorem uncurry (A B C : Prop) : (A → B → C) → ((A ∧ B) → C) :=
fun f ⟨a, b⟩ => f a b
-- Lean 4
-- Intuitionistically valid:
theorem de_morgan_1 (A B : Prop) : ¬(A ∨ B) → ¬A ∧ ¬B := by
intro h
constructor
· intro a; apply h; left; exact a
· intro b; apply h; right; exact b
-- Requires classical logic:
theorem de_morgan_2 (A B : Prop) : ¬A ∧ ¬B → ¬(A ∨ B) := by
intro ⟨na, nb⟩ h
cases h with
| inl a => exact na a
| inr b => exact nb b
| Logic | Type | Proof Term | |-------|------|------------| | P → Q | P → Q | λx:P. M | | P ∧ Q | P × Q | (M, N) | | P ∨ Q | P + Q | inl(M) \| inr(N) | | ⊤ | Unit | () | | ⊥ | Empty | (no terms) | | ¬P | P → ⊥ | λx:P. absurd | | ∀x. P(x) | Π(x:A). P(x) | λx. M | | ∃x. P(x) | Σ(x:A). P(x) | (a, M) |
intro -- Introduce hypothesis (λ)
exact -- Provide exact proof
constructor -- Build ∧ or ∃
left/right -- Build ∨
cases -- Case split on ∨ or ∃
apply -- Function application
❌ Using classical logic unnecessarily: LEM makes proofs non-constructive ✅ Prefer constructive proofs when possible (extract programs)
❌ Confusing provability and truth: ¬¬P doesn't imply P intuitionistically ✅ Understand difference between classical and intuitionistic logic
❌ Ignoring computational content: Proofs are programs! ✅ Exploit Curry-Howard to extract verified programs
lambda-calculus.md - Untyped foundationtype-systems.md - Typed λ-calculusdependent-types.md - Dependent functions (∀) and pairs (∃)formal/lean-proof-basics.md - Practical proof developmentprogram-verification.md - Using proofs to verify programsLast Updated: 2025-10-25 Format Version: 1.0 (Atomic)