... we can simplify Lean's built-in Nats automatically.
endOldNats-- Now, we are using Lean's built-in natural numbers.example:(2*2:Nat)=4:=byn:Nat⊢ 2*2=4rflAll goals completed! 🐙
Doing so is very helpful for large numbers — we would not want to write out
the hundreds or thousands of rewrite steps needed for proving examples like the
following!
Of course, rfl still can't close goals where the values of the terms are unknown.
example(nm:Nat)(h:n=m):n=m:=byn✝:Natn:Natm:Nath:n=m⊢ n=m-- `rfl` will not work here!-- First rewrite the goal with `h`; then the two sides are identical.rw[hn✝:Natn:Natm:Nath:n=m⊢ m=m]All goals completed! 🐙
You can also use rw? to look for theorems to rewrite by.
example(nm:Nat):n+m=m+n:=byn✝:Natn:Natm:Nat⊢ n+m=m+nTry this:[apply]rw [Nat.add_comm]-- no goalsrw?All goals completed! 🐙
Just because rw? suggests a theorem does not mean that it will be useful;
choose carefully from its suggestions (if at all).
example(nmk:Nat):(n+m)+k=m+(n+k):=unsolved goalsn✝nmk:Nat⊢ n+k+m=m+(n+k)byn✝:Natn:Natm:Natk:Nat⊢ n+m+k=m+(n+k)-- lots of suggestions to look through here!Try this:[apply]rw [Nat.add_right_comm]-- n+k+m=m+(n+k)Try this:[apply]rw [Nat.add_assoc]-- n+(m+k)=m+(n+k)Try this:[apply]rw [Nat.add_left_comm]-- n+m+k=n+(m+k)Try this:[apply]rw [Nat.add_comm]-- k+(n+m)=m+(n+k)Try this:[apply]rw [← Nat.add_assoc]-- n+m+k=m+n+kTry this:[apply]rw [Nat.eq_iff_testBit_eq]-- ∀(i:Nat),(n+m+k).testBiti=(m+(n+k)).testBitifound an applicable rewrite lemma, but the corresponding tactic failed:(expose_names; rw [propext(Nat.ext_div_mod_iffn_1(n+m+k)(m+(n+k)))])-- (n+m+k)/n_1=(m+(n+k))/n_1∧(n+m+k)%n_1=(m+(n+k))%n_1It may be possible to correct this proof by adding type annotations, explicitly specifying implicit arguments, or eliminating unnecessary function abstractions.Try this:[apply]rw [Nat.le_antisymm_iff]-- n+m+k≤m+(n+k)∧m+(n+k)≤n+m+kTry this:[apply]rw [← Std.PRange.Nat.succMany_eq]-- Std.PRange.succManyk(n+m)=m+(n+k)found an applicable rewrite lemma, but the corresponding tactic failed:(expose_names; rw [← BitVec.cpopNatRec_allOnesNat.le.refl])-- (BitVec.allOnesk).cpopNatReck(n+m)=m+(n+k)It may be possible to correct this proof by adding type annotations, explicitly specifying implicit arguments, or eliminating unnecessary function abstractions.found an applicable rewrite lemma, but the corresponding tactic failed:(expose_names; rw [← Nat.add_sub_sub_cancelNat.le.refl])-- k+(n+m)-(k-k)=m+(n+k)It may be possible to correct this proof by adding type annotations, explicitly specifying implicit arguments, or eliminating unnecessary function abstractions.Try this:[apply]rw [← Nat.add_left_max_self]-- max(n+m+k)k=m+(n+k)Try this:[apply]rw [← Nat.add_right_max_self]-- max(n+m+k)(n+m)=m+(n+k)Try this:[apply]rw [← Nat.succ_add_sub_one]-- (n+m).succ+k-1=m+(n+k)Try this:[apply]rw [← Nat.max_add_left_self]-- maxk(n+m+k)=m+(n+k)Try this:[apply]rw [← Nat.max_add_right_self]-- max(n+m)(n+m+k)=m+(n+k)Try this:[apply]rw [← Nat.add_succ_sub_one]-- n+m+k.succ-1=m+(n+k)Try this:[apply]rw [← Nat.add_eq]-- (n+m).addk=m+(n+k)Try this:[apply]rw [← Rat.ofNat_eq_ofNat]-- OfNat.ofNat(n+m+k)=OfNat.ofNat(m+(n+k))Try this:[apply]rw [← Nat.compare_eq_eq]-- compare(n+m+k)(m+(n+k))=Ordering.eqrw?n✝:Natn:Natm:Natk:Nat⊢ n+k+m=m+(n+k)
In Lean proofs, long rw chains are useful, but they are sometimes
hard to read because the intermediate goals are invisible.
The calc tactic writes down the intermediate goals of a proof, and
allows us to specify exactly which rewrite rules to apply at each step. It is designed
to mimic the style of proofs in mathematics textbooks, which will often look something like this:
n + (m + k)
= (n + m) + k ... [by associativity of addition]
= (m + n) + k ... [by commutativity of addition]
= m + (n + k) ... [by associativity of addition]
Now, the same theorem written with calc.
Note how each intermediate goal is visible in the source.
example(nmk:Nat):n+(m+k)=m+(n+k):=byn✝:Natn:Natm:Natk:Nat⊢ n+(m+k)=m+(n+k)calcn+(m+k)/- one side of the goal is the argument to `calc`...
... and each subsequent line is a transformation, with a tactic. -/n+(m+k)=(n+m)+k:=byn✝:Natn:Natm:Natk:Nat⊢ n+(m+k)=n+m+krw[Nat.add_assocn✝:Natn:Natm:Natk:Nat⊢ n+(m+k)=n+(m+k)]All goals completed! 🐙(n+m)+k=(m+n)+k:=byn✝:Natn:Natm:Natk:Nat⊢ n+m+k=m+n+krw[Nat.add_commnmn✝:Natn:Natm:Natk:Nat⊢ m+n+k=m+n+k]All goals completed! 🐙/- once a line matches the other side of the equality in the main goal
(in this case `m + (n + k)`), the calc tactic succeeds. -/(m+n)+k=m+(n+k):=byn✝:Natn:Natm:Natk:Nat⊢ m+n+k=m+(n+k)rw[Nat.add_assocn✝:Natn:Natm:Natk:Nat⊢ m+(n+k)=m+(n+k)]All goals completed! 🐙
We can also write the proof like this to be a bit more concise:
We need to unfold the underlying definitions of
addThrice and addTwice so that rw, which only operates on syntax,
can see the addition.
We can do this using the rw tactic.
example(n:Nat):addThricen=n+addTwicen:=byn✝:Natn:Nat⊢ addThricen=n+addTwicen-- `rw [addThrice]` unfolds `addThrice`, replacing it with its definitionrw[addThricen✝:Natn:Nat⊢ n+n+n=n+addTwicen]n✝:Natn:Nat⊢ n+n+n=n+addTwicen-- this likewise unfolds `addTwice`rw[addTwicen✝:Natn:Nat⊢ n+n+n=n+(n+n)]n✝:Natn:Nat⊢ n+n+n=n+(n+n)-- Now, proving our goal only requires associativity of additionsrw[Nat.add_assocn✝:Natn:Nat⊢ n+(n+n)=n+(n+n)]All goals completed! 🐙
Rewriting can also be used in places where rfl can't, like hypotheses.
declaration uses `sorry`example(nm:Nat)(h:2*n=m*2):n+n=m+m:=byn✝:Natn:Natm:Nath:2*n=m*2⊢ n+n=m+m-- use rw? to construct the proofsorryAll goals completed! 🐙
Unfolding should not be overused; simplification rules are (still) useful proof engineering.
example:(funx=>x+0)n=n:=byn:Nat⊢ (funx=>x+0)n=ndsimponlyn:Nat⊢ n+0=n-- applies the function to its argumentrw[Nat.add_zeron:Nat⊢ n=n]All goals completed! 🐙
If we did not simplify here before attempting to rewrite, we would get an error:
example:(funx=>x+0)n=n:=byn:Nat⊢ (funx=>x+0)n=nrw[Tactic `rewrite` failed: Did not find an occurrence of the pattern?n+0in the target expression(funx=>x+0)n=nn:Nat⊢ (funx=>x+0)n=nNat.add_zeron:Nat⊢ (funx=>x+0)n=n]n:Nat⊢ (funx=>x+0)n=n
Tactic `rewrite` failed: Did not find an occurrence of the pattern?n+0in the target expression(funx=>x+0)n=nn:Nat⊢ (funx=>x+0)n=n