We reopen the namespace from the previous chapter to group this chapter's
definitions and theorems with the custom natural-number development and keep
their names distinct from the standard library.
Now let's review what we learned in Basics using some
quiz questions and an exercise.
namespaceNatPlayground.Nat
Quiz
Recall the definition of or, which has notation || and is not
marked @[irreducible]:
def or (b1 : Bool) (b2 : Bool) : Bool :=
match b1 with
| true => true
| false => b2
To prove the following theorem, which tactics will we need besides
rfl?
We will introduce proofs by induction on natural numbers, first motivating
why induction is needed, and then explaining what it is and how you do it
in Lean.
For the add_zero simplification rule, we were able to prove that zero is a
neutral element for + on the right using just rfl.
But the proof that it is also a neutral element on the left gets stuck...
example(n:Nat):zero+n=n:=byn:Nat⊢ zero+n=nTactic `rfl` failed: The left-hand sidezero+nis not definitionally equal to the right-hand sidenn:Nat⊢ zero+n=nrfln:Nat⊢ zero+n=n-- doesn't work here!
Tactic `rfl` failed: The left-hand sidezero+nis not definitionally equal to the right-hand sidenn:Nat⊢ zero+n=n
And reasoning by cases using cases on n doesn't get us much
further: the branch of the case analysis where we assume n = zero
goes through just fine, but in the branch where n = n' + 1 for
some n' we get stuck in exactly the same way.
As you've probably noticed, a common pattern in Lean proofs is rewrite [...]
followed by rfl. Lean also provides a tactic that combines these two steps: rw [...]
will automatically close the goal if the rewrite makes the goal true by
definition. For example, instead of
rewrite [double_zero]; rfl
we could write this:
rw [double_zero]
If rw leaves a goal that looks definitionally true, try adding rfl
after it.
To use add_comm at the point where we need it, we can supply
explicit arguments: rw [add_comm n m] tells Lean exactly which
+ to rewrite. (We can also use have to establish the specific
equation we want, then rewrite with it.)
The same trick also works for match expressions. For example, suppose we start with
defisZero(n:Nat):Bool:=matchnunexpected end of input; expected 'with'
Lean can generate the missing branches:
defisZero(n:Nat):Bool:=matchnwith|.zero=>don't know how to synthesize placeholdercontext:n:Nat⊢ Bool_|.succn=>don't know how to synthesize placeholdercontext:n✝n:Nat⊢ Bool_
One note: Sometimes the variables the code action chooses are not ideal,
so you might want to change them.
By default, rewrite and rw rewrite left to right, i.e.,
they transform the goal (or a hypothesis) from the form on
the left side of the equality to the right side. To rewrite from
right to left, use rewrite [← h] or rw [← h], where ← is entered
as \l or \<-.
These exercises state facts that will be used later.
We don't need to work them in class.
Exercise★★★(mul_comm)
Use have (or rw with explicit arguments) to help prove
add_shuffle3. You don't need to use induction.
Take a piece of paper. For each of the following theorems, first
think about whether (a) it can be proved using only
simplification and rewriting, (b) it also requires case
analysis (cases), or (c) it also requires induction. Write
down your prediction. Then fill in the proof. (There is no need
to turn in your piece of paper; this is just to encourage you to
reflect before you hack!)