Chapters

Your First Proofs

A proposition is a type, and a proof is a value of that type.

13.1 The world of Prop

Ordinary programs use Bool for branching. In proofs, Prop describes the kind of propositions. P: Prop means that P is a proposition.

Just as x: A is a value of type A, if P is a proposition, p: P is its proof. This perspective is called the Curry–Howard correspondence.

ch13-keep.taut
import std::Base

theorem keep(A: Prop, p: A): A {
  p
}

// Return the evidence already supplied as an argument.
✓ Check passed
✓ theorem keep : ∀ A: Prop. (A) -> A

keep says that, given evidence p for A, you can return evidence for A. The body p already has the required type, making it a valid proof.

ok      keep

13.2 Build a proof of “and”

A ∧ B is a proposition containing evidence of both sides. AndIntro(a, b) combines two pieces of evidence. And::left and And::right extract the respective sides.

ch13-and.taut
import std::Base

theorem and_swap(A: Prop, B: Prop, p: A ∧ B): B ∧ A {
  AndIntro(And::right(p), And::left(p))
}

// Project both pieces of evidence and rebuild them in reverse order.
✓ Check passed
✓ theorem and_swap : ∀ A: Prop. ∀ B: Prop. (And(A, B)) -> And(B, A)

Extract evidence for B and A from p, then rebuild it in the opposite order. Although the notation resembles mathematical prose, the result is a typed term.

13.3 Functions prove implication

ch13-compose.taut
import std::Base

theorem compose(
  A: Prop,
  B: Prop,
  C: Prop,
  f: (A) -> B,
  g: (B) -> C
): (A) -> C {
  a => g(f(a))
}

// Compose two implications as ordinary functions.
✓ Check passed
✓ theorem compose : ∀ A: Prop. ∀ B: Prop. ∀ C: Prop. ((A) -> B) -> ((B) -> C) -> (A) -> C

If f constructs evidence for B from evidence for A, and g constructs evidence for C from evidence for B, then a => g(f(a)) proves the implication from A to C. Function composition becomes a logical inference rule.

Exercise 13.1: What body proves (A) -> A?