Formal Software Verification

2.6. Sets🔗

Chapter 3 of HTPIwL develops proofs about sets. A set of elements of a type α is determined by which elements belong to it, so the membership predicate determines the set. In Lean, we take this as the definition.

def Set (α : Type) : Type := α Prop

Every element of a set comes from the fixed type α, and this typing discipline blocks Russell's paradox.B. Russell, letter to Frege, 16 June 1902. In J. van Heijenoort, From Frege to Gödel: A Source Book in Mathematical Logic, 1879–1931, Harvard University Press, 1967, pp. 124–125. Naive set theory admits a set for every property. Take R to be the set of all sets that are not elements of themselves. Then R ∈ R holds exactly when R ∉ R, which is a contradiction, and the theory collapses. In Lean, a set s : Set α contains only elements of α, and s itself has type Set α, not α, so the expression s ∈ s is not well typed. There is no way to state the property that defines R or to form the collection, so the paradox does not arise.

Nothing so far gives the symbol ∈ a meaning at our sets, and Lean does not build one in. A type class declares an operation and leaves it without meaning, and an instance declaration supplies the meaning at one type. The notation x ∈ s reaches ours in three steps, and each step lives in a different place.

The symbol is ordinary notation, declared in the core module Init.Notation. It abbreviates an application and nothing more.

notation:50 a:50 " ∈ " b:50 => Membership.mem b a

The name Membership.mem on the right is the single field of a class declared in the core module Init.Prelude. The class fixes the shape of the operation, taking the type of the elements and the type of the container, and it gives no definition.

class Membership (α : outParam (Type u)) (γ : Type v) where
  mem : γ → α → Prop

The container comes first in mem and second in the notation, so x ∈ s abbreviates Membership.mem s x.

The third step is ours. When Lean elaborates x ∈ s, it searches the registered instances for one whose container type matches the type of s. Set α is a definition of this lecture, so that search finds nothing and the notation fails to elaborate. The instance below ends the search and gives mem its definition at Set α. In this instance and the following ones, Lean binds the free type variable α automatically.

instance : Membership α (Set α) := fun s a => s a

Printing a membership with the notation turned off shows the two steps at once, the expansion of the symbol and the instance that the elaborator found.

set_option pp.notation false in fun α s x => Membership.mem s x : (α : Type) Set α α Prop#check fun (α : Type) (s : Set α) (x : α) => x s
fun α s x => Membership.mem s x : (α : Type)  Set α  α  Prop

With the instance in scope, x ∈ s is the application s x by definition, so the two are interchangeable and rfl proves them equal.

example (α : Type) (s : Set α) (x : α) : (x s) = s x := rfl

The three steps split between two of the components of Figure 1.1. The macro expander performs the first, replacing the symbol by the application, and the elaborator performs the third, choosing the instance from the type of s.

A set given by a property is the predicate itself, and a membership proof is a proof of the property. Mathematical notation writes such a set in set-builder notation, as the set of all n such that ∃ k, n = 2 * k. Lean core has no set-builder notation, so we write the predicate directly.

def Evens : Set Nat := fun n => k, n = 2 * k example : (6 : Nat) Evens := 3, rfl

The inclusion s ⊆ t states that every element of s belongs to t.

instance : HasSubset (Set α) := fun s t => x, x s x t

The notation unfolds to its definition. A hypothesis h : s ⊆ t applies to an element and a membership proof.

example (α : Type) (s t : Set α) (h : s t) (x : α) (hx : x s) : x t := h x hx

An inclusion is a universally quantified implication, so its proofs begin by considering an arbitrary element together with the assumption that it belongs to the left side. Union and intersection apply the connectives of Lecture 1 pointwise.

instance : Union (Set α) := fun s t => fun x => x s x t instance : Inter (Set α) := fun s t => fun x => x s x t

Both notations unfold likewise, so the proof terms of Lecture 1 build and use memberships directly.

example (α : Type) (s t : Set α) (x : α) (hx : x s) : x s t := Or.inl hx example (α : Type) (s t : Set α) (x : α) (hx : x s t) : x t := hx.right

Membership in an intersection is by definition a conjunction, so the projections of Lecture 1 apply to it.

theorem inter_subset_left (α : Type) (s t : Set α) : s t s := α:Types:Set αt:Set αs t s α:Types:Set αt:Set αx:αhx:x s tx s All goals completed! 🐙

Membership in a union is a disjunction, so the tactic cases splits it.

theorem union_subset_swap (α : Type) (s t : Set α) : s t t s := α:Types:Set αt:Set αs t t s α:Types:Set αt:Set αx:αhx:x s tx t s cases hx with α:Types:Set αt:Set αx:αh:x sx t s All goals completed! 🐙 α:Types:Set αt:Set αx:αh:x tx t s All goals completed! 🐙

Two sets with the same elements are equal. Proving such an equality requires extensionality principles beyond the logic presented so far, so we state set identities as inclusions.

2.6.1. Examples🔗

The examples below prove memberships and inclusions directly from the definitions. Each inclusion proof begins by introducing an element and its membership hypothesis, and the notations unfold to the connectives and quantifiers of the previous sections.

Example 1. A set given by a predicate contains an element exactly when the predicate holds at it. The witness 3 proves that 9 is a square.

def Squares : Set Nat := fun n => k, n = k * k example : (9 : Nat) Squares := 3, rfl

Example 2. Inclusion is reflexive. The proof introduces an element and its membership hypothesis and returns the hypothesis unchanged.

example (α : Type) (s : Set α) : s s := α:Types:Set αs s α:Types:Set αx:αhx:x sx s All goals completed! 🐙

Example 3. The union contains its left side. Membership in the union is a disjunction, and Or.inl picks the left side.

example (α : Type) (s t : Set α) : s s t := α:Types:Set αt:Set αs s t α:Types:Set αt:Set αx:αhx:x sx s t All goals completed! 🐙

Example 4. Intersection commutes as an inclusion. Membership in the intersection is a conjunction, and the anonymous constructor swaps its parts.

example (α : Type) (s t : Set α) : s t t s := α:Types:Set αt:Set αs t t s α:Types:Set αt:Set αx:αhx:x s tx t s All goals completed! 🐙

Example 5. The union contains the intersection.

example (α : Type) (s t : Set α) : s t s t := α:Types:Set αt:Set αs t s t α:Types:Set αt:Set αx:αhx:x s tx s t All goals completed! 🐙

Example 6. When t and u both contain s, their intersection contains s.

example (α : Type) (s t u : Set α) (h1 : s t) (h2 : s u) : s t u := α:Types:Set αt:Set αu:Set αh1:s th2:s us t u α:Types:Set αt:Set αu:Set αh1:s th2:s ux:αhx:x sx t u All goals completed! 🐙

Example 7. When u contains both sides of a union, u contains the union. The tactic cases splits the disjunction.

example (α : Type) (s t u : Set α) (h1 : s u) (h2 : t u) : s t u := α:Types:Set αt:Set αu:Set αh1:s uh2:t us t u α:Types:Set αt:Set αu:Set αh1:s uh2:t ux:αhx:x s tx u cases hx with α:Types:Set αt:Set αu:Set αh1:s uh2:t ux:αhs:x sx u All goals completed! 🐙 α:Types:Set αt:Set αu:Set αh1:s uh2:t ux:αht:x tx u All goals completed! 🐙

Example 8. Union with a fixed set preserves inclusion.

example (α : Type) (s t u : Set α) (h : s t) : s u t u := α:Types:Set αt:Set αu:Set αh:s ts u t u α:Types:Set αt:Set αu:Set αh:s tx:αhx:x s ux t u cases hx with α:Types:Set αt:Set αu:Set αh:s tx:αhs:x sx t u All goals completed! 🐙 α:Types:Set αt:Set αu:Set αh:s tx:αhu:x ux t u All goals completed! 🐙

Example 9. The empty set, whose membership predicate is False at every element, is a subset of every set. False.elim closes the goal.

def EmptySet (α : Type) : Set α := fun _ => False example (α : Type) (s : Set α) : EmptySet α s := α:Types:Set αEmptySet α s α:Types:Set αx:αhx:x EmptySet αx s All goals completed! 🐙

Example 10. Every set is a subset of the universal set, whose membership predicate is True at every element.

def UnivSet (α : Type) : Set α := fun _ => True example (α : Type) (s : Set α) : s UnivSet α := α:Types:Set αs UnivSet α α:Types:Set αx:α_hx:x sx UnivSet α All goals completed! 🐙