Final #1 : Binary Relations and Equality
This question will test your ability to build on what you have already learned. You are given a lesson on how binary relations (which you can think of as sets of pairs of values) are specified by two-place predicates along with rules for forming proofs of such predicates.
Representing Relations as Two-Place Predicates
We've seen how to represent sets, in set theory, as logical predicates, and set operations as logical operations on underlying predicates. For example set union is disjunction of respective membership predicates. Different sets of objects of some type, α, are specified by different predicates, all of one argment: of type α → Prop in Lean's type theory.
To represent not a set but a binary relation, r, on objects of some type, α, we use predicates with two arguments: r : α → α → Prop. You can think of a relation as specifying a set of pairs of values: those pairs that satisfy the given predicate. As an example, r could be the less than or the equals relation on natural numbers.
Now we will often want to define our own types, 2-place predicates on values of such types, and axioms for constructing membership proofs.
Example: Defining a Binary likes Relation on Dogs
Let's consider a simple example. We'll define a type called Dog; three dogs of this type (rover, fido, and iris); a binary likes relation on dogs; and, finally, we'll specify what proofs can be constructed: that rover likes fido, fido likes iris, and iris likes rover.
Inductive Families of Propositions and Proofs
In Lean we can write such a specification in the form of what we call an inductive family of propositions and associated proofs, as follows. The name of the relation (defined next) is likes. It takes two dog arguments and yields a proposition that we read as asserting that the first dog likes the second.
Here's our dog type for purposes of this example. With this type in hand, we'll be able to define a binary relation on these dogs.
inductive dog: Type
dog : Type: Type 1
Type
| rover: dog
rover
| fido: dog
fido
| iris: dog
iris
open dog
Here's the important new idea: we specify a family of likes propositions using inductive, and the set of associated proofs are given by the constructors of this type. Namely, we can form a proposition that any dog likes any other, but the only proofs we have, and thus the only pairs of dogs that are in the relation are (rover, fido), (fido, iris), and (iris rover).
inductive likes: dog → dog → Prop
likes : dog: Type
dog → dog: Type
dog → Prop: Type
Prop
| rlf: likes rover fido
rlf : likes: dog → dog → Prop
likes rover: dog
rover fido: dog
fido
| fli: likes fido iris
fli : likes: dog → dog → Prop
likes fido: dog
fido iris: dog
iris
| ilr: likes iris rover
ilr : likes: dog → dog → Prop
likes iris: dog
iris rover: dog
rover
open likes
We can now form any likes proposition using this proposition builder.
likeslikes: dog → dog → Propirisiris: dogfidofido: doglikeslikes: dog → dog → Propirisiris: dogroverrover: doglikeslikes: dog → dog → Propirisiris: dogirisiris: dog
Moreover, we can state and prove propositions about dogs liking each other.
example :example: likes rover irislikeslikes: dog → dog → Proproverrover: dogiris :=iris: dog-- oops, no proof of this example : ¬ likes rover iris := λ h => nomatch h -- ok that's provable example : likes iris rover := ilr -- yep, there's a proof example : likes rover fido ∧ likes iris rover := ⟨ rlf, ilr ⟩ --true!
Example: A Relation Associating Each Nat With Its Square
Inductive families can be used to formalize all manner of important and interesting relations. Here's another example were we define a relation that associates each natural number with its square.
inductive square : Nat → Nat → Prop
| sqr (a b : Nat) : b = a ^ 2 → square a b
open square
You can read this definition as saying that square is a binary relation on natural numbers (first line), and (constructor). It is applied to any two natural numbers, a and b, along with a proof that b = a ^ 2 to construct a proof of square a b.
def sqr11 : square 1 1 := sqr 1 1 rfl
def sqr39 : square 3 9 := sqr 3 9 rfl
example : ¬ square 3 8 := λ h => nomatch h -- proof by negation
example : square 1 1 ∧ square 3 9 := ⟨ sqr11, sqr39 ⟩
Exam Question #1
Express the following proposition in English, complete the formal proof of it, and briefly explain in English how you proved it.
example : ∃ n, square n 4 := Exists.intro 2 () -- fill in _ /- English language translation of propostion here: -/
The Binary Equality Relation on Objects of a Type α
With these concepts under our belts we can now understand equality as a binary relation. In fact, in Lean, it's a polymorphic binary relation. Given any type, α, there is a binary equality relation on values of type α. Equality is defined as a polymorphic inductive family, called Eq with one type parameter, two value parameters, and one proof constructor, called refl. We'll unpack all of this in steps.
The Inductive Family of Propositions and Proofs
First, let's first see how applying Eq to a type, α, yields the type of binary binary relations, represented as two-argument predicates, on α: that is, as a function that takes two arguments of type α and that returns the proposition that they're equal. (NB: We're not yet talking about proofs.)
-- Polymorphic Equality Relation Builder@Eq -- @Eq : {α : Sort u} → α → α → Prop -- Sort u means any type -- The type of binary relations on the natural numbersEq: {α : Sort u_1} → α → α → Prop@EqEq: {α : Type} → α → α → PropNat -- Eq : Nat → Nat → Prop -- The type of binary relation on stringsNat: Type@EqEq: {α : Type} → α → α → PropString -- Eq : String → String → PropString: Type
Next Let's see what propositions result when we apply Eq to a type, α, and two values of that type. We expect a proposition that the two values are equal, and that's exactly what we get.
@EqEq: {α : Type} → α → α → PropNatNat: Type11: Nat2 -- The proposition that 1 = 2 -- We can omit the @ and let Lean infer the type argument2: NatEqEq: {α : Type} → α → α → Prop11: Nat2 -- a false equality proposition2: NatEqEq: {α : Type} → α → α → Prop00: Nat0 -- a true equality proposition0: NatEqEq: {α : Type} → α → α → Prop"Hi""Hi": String"Bob" -- a false equality of strings"Bob": String
Infix Notation: = is Eq
Now note: a = b is just infix notation for Eq a b! In the first of the following definitions, we use Eq as a prefix notation. In the second, we use = as infix notation. To see how to prove an equality proposition, just look at the proof construction rule defined for Eq.
example: ∀ (α : Type) (a : α), a = a
example (α: Type
α : Type: Type 1
Type): ∀ (a: α
a : α: Type
α), Eq: {α : Type} → α → α → Prop
Eq a: α
a a: α
a := λ a: α
a => Eq.refl: ∀ {α : Type} (a : α), a = a
Eq.refl a: α
a
example: ∀ (α : Type) (a : α), a = a
example (α: Type
α : Type: Type 1
Type): ∀ (a: α
a : α: Type
α), a: α
a = a: α
a := λ a: α
a => Eq.refl: ∀ {α : Type} (a : α), a = a
Eq.refl a: α
a
Note that it's a type error in Lean to ask about equality of objects of different types. In the following example, Lean complains not that the types are different but that it tried and failed to convert the first argument, 1, to a corresponding String value. Lean natively lacks a rule for performing such coercions.
EqEq: {α : Type} → α → α → Prop"Hi""Hi": String
Formal Definition of Equality Relation(s) in Lean
So we are now finally ready to meet Lean's definition of the equality relation, Eq, including the means by which one constructs proofs of equality. Here it is, from mathlib (Lean's library of mathematical definitions). We put a ' on the name so as not to clash with Lean's definition.
inductive Eq': {α : Sort u_1} → α → α → Prop
Eq' : α: Sort u_1
α → α: Sort u_1
α → Prop: Type
Prop where
| refl: ∀ {α : Sort u_1} (a : α), Eq' a a
refl (a: α
a : α: Sort u_1
α) : Eq': {α : Sort u_1} → α → α → Prop
Eq' a: α
a a: α
a
Let's dissect this definition. We'll use Lean's definition henceforth. First, we note that the first line uses α without declaring it. Lean 4 is programmed to assume that α is some type. The first line is thus equivalent to the following: inductive Eq (α : Sort u): α → α → Prop.
Eq'Eq': {α : Type} → α → α → Prop22: Nat3 -- Using our definition of Eq'3: NatEqEq: {α : Type} → α → α → Prop22: Nat3 -- Lean supports infix = notation3: Nat
The Introduction Rule for Eq
And what about proofs? How can we prove 1 = 1, for example?
Eq provides a single constructor, refl, with any value, a : α, as an argument. The term, Eq.refl a is defined by this rule to be a proof of a = a. Eq.refl is thus the introduction rule for equality: the rule for constructing a proof an equality. Indeed, it is the only way to create a proof of an equality proposition.
Note that there is no way to form a proof of a = b for different values of a and b, as refl takes only one argument! One thing to note is that Lean reduces terms, such as a and b, when Eq is applied, so Eq.refl will prove a = b if they are the same term when reduced. This allows us to prove equality propositions such as 2 = 1 + 1, because both sides reduce to the same term, 2. Let's assert some equalities and see what we can prove.
example: 1 = 1
example : 1: Nat
1 = 1: Nat
1 := Eq.refl: ∀ {α : Type} (a : α), a = a
Eq.refl 1: Nat
1
example: 1 = 1
example : 1: Nat
1 = 1: Nat
1 := rfl: ∀ {α : Type} {a : α}, a = a
rfl -- Lean infers α = Nat *and* a = 1
example: 2 = 1 + 1
example : 2: Nat
2 = 1: Nat
1 + 1: Nat
1 := rfl: ∀ {α : Type} {a : α}, a = a
rfl -- Lean reduces 1 + 1 to 2, rfl works
example: "Hi" = "Hi"
example : "Hi": String
"Hi" = "Hi": String
"Hi" := Eq.refl: ∀ {α : Type} (a : α), a = a
Eq.refl "Hi": String
"Hi"
example: true = (true || false)
example : true: Bool
true = or: Bool → Bool → Bool
or true: Bool
true false: Bool
false := Eq.refl: ∀ {α : Type} (a : α), a = a
Eq.refl true: Bool
true
-- Three proofs of the same inequality: "proof by negation"
example: 1 ≠ 2
example : 1: Nat
1 ≠ 2: Nat
2 := λ h: 1 = 2
h => nomatch h: 1 = 2
h
example: ¬1 = 2
example : ¬1: Nat
1 = 2: Nat
2 := λ h: 1 = 2
h => nomatch h: 1 = 2
h
example: 1 = 2 → False
example : 1: Nat
1 = 2: Nat
2 → False: Prop
False := λ h: 1 = 2
h => nomatch h: 1 = 2
h
What About rfl?
Ok, so what about this rfl thing we've been using to produce proofs of equality? It's just function that applies Eq.refl to α and a, but now both values are inferred from context. The key difference between rfl and Eq.refl is that rfl takes both the type and the value argument implicitly, whereas refl requires that the value, a, be given explicitly. Both operations return a proof of a = a for any a of any type. The rfl function does so by using Eq.refl. If Lean cannot infer a due to lack of context, you have to use Eq.refl and give a explicitly.
Here you can see that rfl really just infers a and applies Eq.refl to it.
@rfl -- fun {α} {a} => Eq.refl arfl: ∀ {α : Sort u_1} {a : α}, a = aexample :example: 1 = 11 =1: Nat1 :=1: Natrfl -- infers α = Nat, a = 1rfl: ∀ {α : Type} {a : α}, a = aexample :example: 1 = 11 =1: Nat1 :=1: NatEq.reflEq.refl: ∀ {α : Type} (a : α), a = a1 -- infers α = Nat, a = 11: Nat
The Elimination Rule for Equaltiy
We've now seen the introduction rule, refl, for constructing proofs of equality. What is or are the elimination rules? If you have a proof of equality, how can you use it in constructing other proofs? The answer is if you know (have a proof, h : a = b), and you have a proof, pa : P a, of a proposition, P a (formed by applying a predicate P to a), then you can use h : a = b to rewrite your proof of P a into a proof of P b.
Here's an example. If you know Mary is Nice, and Mary is also know as (is equal to) Maire, then you can conclude that Maire is Nice. In other words, if a = b, then you can substitute b for a in any proposition without changing its meaning. Again, informally, if P a (Mary is Nice), and a = b (Mary also known as Maire), then P b (Maire is nice).
This rule, known as the substitutivity of equals, is the elimination ("how to use") rule for proofs of equalities. In Lean it's called Eq.subst, and it's defined as follows. Note that in this definition, motive is just another name for P in our previous example.
@Eq.substEq.subst: ∀ {α : Sort u_1} {motive : α → Prop} {a b : α}, a = b → motive a → motive b
Eq.subst.{u} {α : Sort u} {motive : α → Prop} {a b : α} (h₁ : a = b) (h₂ : motive a) : motive b
In other words, given (1) any type of values, α, (2) any predicate, motive, on values of this type, (3) two values a and b, (4) a proof of a = b, and (5) a proof of motive a (that a satisfies the predicate), then you can by applying this rule obtain a proof that b satisfies it, too. Let's see some examples. Note that the type, predicate, and two values are all inferred, so in a practical application of Eq.subst you only provide a proof of equality and a proof of motive a to get a proof of motive b. (In our example, again, motive is the Nice predicate.)
section variable (Person :Person: TypeType) (Type: Type 1MaryMary: PersonMaire :Maire: PersonPerson) (Person: TypeNice :Nice: Person → PropPerson →Person: TypeProp) (Prop: Typeh₁ :h₁: Mary = MaireMary =Mary: PersonMaire) (Maire: Personh₂ :h₂: Nice MaryNiceNice: Person → PropMary)Mary: Person@Eq.substEq.subst: ∀ {α : Sort u_1} {motive : α → Prop} {a b : α}, a = b → motive a → motive bexample :example: ∀ (Person : Type) (Mary Maire : Person) (Nice : Person → Prop), Mary = Maire → Nice Mary → Nice MaireNiceNice: Person → PropMaire :=Maire: PersonEq.substEq.subst: ∀ {α : Type} {motive : α → Prop} {a b : α}, a = b → motive a → motive bh₁h₁: Mary = Maireh₂ endh₂: Nice Mary
Understand this example! From Mary = Maire and Mary is nice we proved that Maire is nice, and the proof was by substitution of equals for equals, namely Maire for Mary. That's how you'd explain it in English.
Here's another example. It formalizes just what we've said. If α is any type; P is a predicate on values of this type; a and b are values of this type, you have a proof, h, of a = b, and you have a proof, pa : P a, then you can have a proof of P b by applying Eq.subst.
example: ∀ {α : Type} {P : α → Prop} {a b : α}, a = b → P a → P b
example {α: Type
α : Type: Type 1
Type} {P: α → Prop
P : α: Type
α → Prop: Type
Prop} {a: α
a b: α
b : α: Type
α} (h: a = b
h : a: α
a = b: α
b) (pa: P a
pa : P: α → Prop
P a: α
a) : P: α → Prop
P b: α
b :=
Eq.subst: ∀ {α : Type} {motive : α → Prop} {a b : α}, a = b → motive a → motive b
Eq.subst h: a = b
h pa: P a
pa
Aside on Tactics in Lean
In Lean, applying Eq.subst directly can be tricky and doesn't always produce what you want. The reasons are beyond the scope of this class. The upshot is that for us it's better to use a predefined Lean tactic, rw (short for rewrite), that does the work of applying Eq.subst for us.
A tactic in Lean is a proof constructing automation. When providing a term of any type, you cal always switch into tactic mode using the keyword, by, that is followed then by a sequence of tactic applications.
Tactics can take arguments. The rw tactic is given to us from Lean's library. Applied with a proof of an equality, h : a = b, rw [h] rewrites each a in the current goal to b. That's logically sound: after all a = b. This is very helpful if you have a proof of P b. To do a rewrite in the reverse direction, rewriting each b in the current goal to a, justified by the same equality, proof, h : a = b, one writes rw [←h]. Here's an example. Study carefully the starting context, understand how the rewrite should work, and confirm it does.
Place your cursorat the end of the by line, then after each of the tactic lines, so see how the goal and your context change when you apply the rw tactics.
example {example: ∀ {α : Type} {P : α → Prop} {a b : α}, a = b → P a → P bα :α: TypeType} {Type: Type 1P :P: α → Propα →α: TypeProp} {Prop: Typeaa: αb :b: αα} (α: Typeh :h: a = ba =a: αb) (b: αpa :pa: P aPP: α → Propa):a: αPP: α → Propb :=b: α-- use tactic mode to construct required termGoals accomplished! 🐙α: Type
P: α → Prop
a, b: α
h: a = b
pa: P aP bα: Type
P: α → Prop
a, b: α
h: a = b
pa: P aP a-- rewrite b to a in the goalα: Type
P: α → Prop
a, b: α
h: a = b
pa: P aP a-- we already have a proof of the goal; doneGoals accomplished! 🐙
Properties of the Equality Relation
Properties of the Equality Relation
From just the introduction rule (reflexivity) and the elimination rule (substitutability of equals for equals) we can prove that the equality relation has three critical additional properties:
- it's symmetric
- it's transitive
- it's an equivalence relation
Here we will focus on symmetry and transitivity. Being an equivalence relation just means that it reflexive, symmetric, and transitive. We will explain how to prove that equality is symmetric. We will then leave it to you to prove that it is also transitive. That will show that you are conversant with the ideas from this class and from this problem.
Equality is Reflexive
Equality is reflexive by definition: for any (a : α), the term, Eq.refl a, is accepted as a proof of a = a. So if you need a proof of a = a, you can, and indeed must, construct it by applying the Eq.refl *proof constructor to a.
Here's another proof of reflexivity. Of course is just boils down to the refl constructor. It makes Eq reflexive by definition.
See the following code. We hypothesize that α is any type and a is any value of this type. We then prove the proposition that a = a. Be sure to read this example and understand it fully.
example (α : Type): ∀ (a : α), a = a := λ a => Eq.refl a
Theorem: Equality is Symmetric
Our second example is a statement and proof of that the binary equality relation on a type of objects is symmetric.
theorem eq_is_symm {α : Type} {a b : α} : a = b → b = a := λ h =>-- Put cursor here to see contextGoals accomplished! 🐙α: Type
a, b: α
h: a = bb = aα: Type
a, b: α
h: a = bb = bGoals accomplished! 🐙
It's important to put your see the context after we assume h but before we do the rewrite. Observe that the rewrite will write left terms into right terms in the current goal. That makes the goal into b = b. And that is true with a proof by rfl.
Exam Question #2: State and Prove that Equality is Transitive
Formally state and prove that equality is transitive. Finish the proposition (fill in the first underscore "hole") so that it asserts that equality is transitive. Avoid using ∧ in your definition; use → instead. It simplifies the proofs.
Model your answer on the example of symmetry above. You already know how to construct proofs of implications. The new element here is rewriting goals based on known (or assumed) equalities.
Notes: (1) fill in the _ holes. (2) you can and will have to write separate tactic applications indented on separate lines.
theorem eq_rel_trans: {α : Type} → {a b c : α} → (x : ?m.2325) → (x_1 : ?m.2326 x) → ?m.2327 x x_1
eq_rel_trans {α: Type
α : Type: Type 1
Type} {a: α
a b: α
b c: α
c : α: Type
α} :
_: Sort (imax u_1 u_2 u_3)
_ -- fill with proposition: equality is transitive
| _, _ => by
_ -- fill in your proof of it here
Exam Question #3
You know that the Nat.succ function takes any natural number and returns its successor. You are to define the corresponding binary relation, as an inductive family. It will specify a set of pairs of natural number values, (a, sa), where sa = Nat.succ a. Then prove (1) the pair, (2, 3) is in the relation, and the pair (2,4) is not.
-- Your answer here