3. Tactics🔗

3.1. Tactics Cheatsheet🔗

Proof state

Tactic

New proof state

⊢ P → Q

intro hP

hP : P
⊢ Q

⊢ P → Q → R

intro hP hQ

hP : P
hQ : Q
⊢ R

p : α → Prop
⊢ ∀ (x : α), f x

intro x

f: α → Prop
x : α
⊢ p x

h : P
⊢ P

exact h

no goals 🎉

h : P
⊢ P

assumption

no goals 🎉

h : P → Q
⊢ P

apply h

⊢ Q

h₁ : P → Q
h₂ : Q → R
⊢ R

apply h₂ h₁

h₁ : P → Q
h₂ : Q → R
⊢ P

⊢ P ∧ Q → P

tauto oder tauto!

no goals 🎉

⊢ true

triv

no goals 🎉

h : P
⊢ Q

exfalso

h : P
⊢ false

⊢ P

by_contra h

h : ¬P
⊢ false

⊢ P

by_cases h : Q

h : Q
⊢ P
h : ¬Q
⊢ P

h : P ∧ Q
⊢ R

cases' h with hP hQ

hP : P
hQ : Q
⊢ R

h : P ∧ Q
⊢ R

obtain ⟨hP, hQ⟩ := h

hP : P
hQ : Q
⊢ R

h : P ∨ Q
⊢ R

cases' h with hP hQ

hP : P
⊢ R
hQ : Q ⊢ R

h : false
⊢ P

cases h

no goals 🎉

⊢ : P → false

change ¬P

⊢ ¬P

⊢ P ∧ Q

constructor

⊢ P
⊢ Q

⊢ P ↔ Q

constructor

⊢ P → Q
⊢ Q → P

⊢ P ↔ P oder
⊢ P = P

rfl

no goals 🎉

h : P ↔ Q
⊢ P

rw h

h : P ↔ Q
⊢ Q

h : P ↔ Q
hP : P

rw h at hP

h : P ↔ Q
hP : Q

h : P ↔ Q
⊢ Q

rw ← h

h : P ↔ Q
⊢ P

h : P ↔ Q
hQ : Q

rw ← h at hQ

h : P ↔ Q
hQ : P

⊢ P ∨ Q

left

⊢ P

⊢ P ∨ Q

right

⊢ Q

⊢ 2 + 2 < 5

norm_num

no goals 🎉

p : α → Prop
y : α
⊢ ∃ (x : α), f x

use y

p : α → Prop
y : α
⊢ f y

x y : ℝ
⊢ x + y = y + x

ring

no goals 🎉

p : α → Prop
⊢ ∀ (x : α), p x

intro x

p : α → Prop
x : α
p x

h₁ : a < b
h₂ : b ≤ c
⊢ a < c

linarith

no goals 🎉

h : P
⊢ Q

clear h

⊢ Q

p : ℕ → Prop
h : ∀ (n : ℕ), p n
⊢ P

specialize h 13

p : ℕ → Prop
h : p 13
⊢ P

p : ℕ → ℕ → Prop
h : ∀ (n : ℕ), ∃ (m : ℕ), f n m

obtain ⟨m, hm⟩ := h 27

f : ℕ → ℕ → Prop
h : ∀ (n : ℕ), ∃ (m : ℕ), f n m
m : ℕ
hm : f 27 m

⊢ R

have h : P ↔ Q

⊢ P ↔ Q
h : P ↔ Q
⊢ R

h₁ : a < b
h₂ : b < c
⊢ a < c

apply?

no goals 🎉
Try this:
exact lt_trans h₁ h₂

hQ : Q
⊢ P ∧ Q

refine ⟨ _, hQ ⟩

hQ : Q
⊢ P

⊢ P ∨ Q → R

rintro (hP | hQ)
=
intro h
cases h with hP hQ

hP : P
⊢ R
hQ : Q
⊢ R

⊢ P ∧ Q → R

rintro ⟨hP , hQ⟩
=
intro h
cases h with h1 h2

hP : P
hQ : Q
⊢ R

h : P ∧ Q ∨ P ∧ R
⊢ S

rcases h with (⟨hP1,hQ⟩|⟨hP2,hR⟩)

hP1 : P
hQ : Q
⊢ S
hP2 : P
hR : R
⊢ S

⊢ n + 0 = n

simp

no goals 🎉

h : n + 0 = m
⊢ P

simp at h

h : n = m
⊢ P

3.2. apply🔗

Summary: If we have the goal ⊢ Q, and a proof of h : P → Q, we only need to find a proof for P. This transition happens by apply h.

Proof state

Tactic

New proof state

h : P → Q
⊢ Q

apply h

h : P → Q
⊢ P

h : ¬P
⊢ False

apply h

h : ¬P
⊢ P

h₁ : P → Q
h₂ : Q → R
⊢ R

apply h₂ h₁

h₁ : P → Q
h₂ : Q → R
⊢ P

The apply-tactics works iteratively. This means that if apply h makes no progress, it uses the placeholder _ and tries to make apply h _.

Remarks:

  • apply works up to equality by definition. This can be seen in the example above, where ¬P ↔ (P → False) is true by definition.

  • apply h is frequently identical to refine ?_.

  • If the use of apply closes the current goal, you might as well use exact instead of apply.

example (hP : P) (hPQ : P Q) (hPQR : P Q R) : R := P:Sort ?u.27Q:Sort ?u.31R:Sort ?u.37hP:PhPQ:PQhPQR:PQRR P:Sort ?u.27Q:Sort ?u.31R:Sort ?u.37hP:PhPQ:PQhPQR:PQRPP:Sort ?u.27Q:Sort ?u.31R:Sort ?u.37hP:PhPQ:PQhPQR:PQRQ P:Sort ?u.27Q:Sort ?u.31R:Sort ?u.37hP:PhPQ:PQhPQR:PQRP All goals completed! 🐙 P:Sort ?u.27Q:Sort ?u.31R:Sort ?u.37hP:PhPQ:PQhPQR:PQRQ P:Sort ?u.27Q:Sort ?u.31R:Sort ?u.37hP:PhPQ:PQhPQR:PQRP All goals completed! 🐙 example (n : ) (hn : 0 < n) : n 2 * (n * n) := n:hn:0 < nn2 * (n * n) have h₁ : n n * n := n:hn:0 < nn2 * (n * n) All goals completed! 🐙 n:hn:0 < nh₁:nn * nn * n2 * (n * n) have h₂ (k : ) : k 2 * k := n:hn:0 < nn2 * (n * n) All goals completed! 🐙 All goals completed! 🐙
🔗def
Lean.Parser.Tactic.apply : Lean.ParserDescr

apply e tries to match the current goal against the conclusion of e's type. If it succeeds, then the tactic returns as many subgoals as the number of premises that have not been fixed by type inference or type class resolution. Non-dependent premises are added before dependent ones.

The apply tactic uses higher-order pattern matching, type class resolution, and first-order unification with dependent types.

3.3. apply?🔗

Summary: There are already a lot of proven statements in Mathlib. When using apply?, Mathlib is searched for statements whose types correspond to those of the statement to be proved. If this is not successful, Lean reports a timeout. If successful, it also reports which commands were found. If you click on it, this is inserted in place of apply?.

Examples

Proof state

Tactic

New proof state

h₁ : a < b
h₂ : b < c

apply?

no goals
Try this: exact lt_trans h₁ h₂

example (n : ) : 2 * n = n + n := n:2 * n = n + n All goals completed! 🐙
Try this: exact Nat.two_mul n
🔗def
Lean.Parser.Tactic.apply? : Lean.ParserDescr

Searches environment for definitions or theorems that can refine the goal using apply with conditions resolved when possible with solve_by_elim.

The optional using clause provides identifiers in the local context that must be used when closing the goal.

3.4. assumption🔗

Summary: If a hypothesis is identical to the goal, assumption closes the goal.

Examples

Proof state

Tactic

New proof state

h : P
⊢ P

assumption

no goals

h : ¬P
⊢ P → False

assumption

no goals

Remarks

  • As in other tactics, assumption works up to definitional equality.

  • Here is a trick: If you use <;> after a tactic, the forthcoming tactic is applied to apll goals.

example (hP : P) (hQ : Q) : P Q := P:PropQ:ProphP:PhQ:QPQ P:PropQ:ProphP:PhQ:QPP:PropQ:ProphP:PhQ:QQ P:PropQ:ProphP:PhQ:QPP:PropQ:ProphP:PhQ:QQ All goals completed! 🐙 example (P : Prop) (hP : P) : P := P:ProphP:PP All goals completed! 🐙
🔗def
Lean.Parser.Tactic.assumption : Lean.ParserDescr

assumption tries to solve the main goal using a hypothesis of compatible type, or else fails. Note also the t term notation, which is a shorthand for show t by assumption.

3.5. by_cases🔗

Summary: If you have a term P : Prop as a hypothesis, by_cases hP : P returns two goals. The first one has hP : P, and the second one hP : ¬P. This tactic is thus identical to have hP : P ∨ ¬ P, exact em P, cases hP,. (The second expression is em : ∀ (p : Prop), p ∨ ¬p.)

Examples

Proof state

Tactic

New proof state

⊢ P

by_cases h : Q

h : Q
⊢ P
h : ¬Q ⊢ P

x : Bool
⊢ x = True ∨ x = False

by_cases x = True

x: bool
h: x = True
⊢ x = True ∨ x = False
x: bool
h: ¬x = True
⊢ x = True ∨ x = False

In the second example, we use a variable of type bool This is defined as follows:

🔗inductive type
Bool : Type

Bool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages.

Constructors

false : Bool

The boolean value false, not to be confused with the proposition False.

true : Bool

The boolean value true, not to be confused with the proposition True.

example (P Q : Prop) (hP: P Q) ( hP' : ¬P Q) : Q := P:PropQ:ProphP:PQhP':¬PQQ P:PropQ:ProphP:PQhP':¬PQh:PQP:PropQ:ProphP:PQhP':¬PQh:¬PQ P:PropQ:ProphP:PQhP':¬PQh:PQ All goals completed! 🐙 P:PropQ:ProphP:PQhP':¬PQh:¬PQ All goals completed! 🐙
🔗def
«tacticBy_cases_:_» : Lean.ParserDescr

by_cases (h :)? p splits the main goal into two cases, assuming h : p in the first branch, and h : ¬ p in the second branch.

Notes

  • Apparently, the by_cases tactic (just like by_contra) assumes that a statement is either true or false. This is also known as the law of excluded middle. In mathematics, proofs that do not use this rule are called constructive.

  • For terms of type Prop, the tactic tauto (or tauto!) can draw various conclusions from a truth table.

3.6. by_contra🔗

Summary

The by_contra tactic provides a proof by contradiction. It is therefore assumed (i.e. transformed into a hypothesis) that the statement (after ) is not true, and this must be made to contradict itself, i.e. a proof of false must be found.

Examples

Proof state

Tactic

New proof state

⊢ P

by_contra h

h : ¬P
⊢ False

h: ¬¬P
⊢ P

by_contra hnegP

h: ¬¬P
hnegP: ¬P
⊢ False

Remarks

This tactic is stronger than exfalso. After all, there the goal is only converted to false without adding a new hypothesis. With by_contra, the new goal is also false, but there is still a new hypothesis.

example (P Q : Prop) (hP: P Q) ( hP' : ¬P Q) : Q := P:PropQ:ProphP:PQhP':¬PQQ P:PropQ:ProphP:PQhP':¬PQh:PQP:PropQ:ProphP:PQhP':¬PQh:¬PQ P:PropQ:ProphP:PQhP':¬PQh:PQ All goals completed! 🐙 P:PropQ:ProphP:PQhP':¬PQh:¬PQ All goals completed! 🐙

3.7. calc🔗

Summary: As the word suggests, calc is about concrete calculations. This is not a tactic, but a lean mode. This means that you can enter this mode (with the word calc) and enter calculation steps and proofs that each individual calculation step is correct.

Examples

Here is a proof of the first binomial formula that only comes about by rewriting of calculating properties from the mathlib.

example (n : ): (n+1)^2 = n^2 + 2*n + 1 := n:(n + 1) ^ 2 = n ^ 2 + 2 * n + 1 have h : n + n = 2 * n := n:(n + 1) ^ 2 = n ^ 2 + 2 * n + 1 nth_rewrite 1 [n:1 * n + n = 2 * n] nth_rewrite 2 [n:1 * n + 1 * n = 2 * n] n:(1 + 1) * n = 2 * n All goals completed! 🐙 calc (n+1)^2 = (n+1) * (n+1) := n:h:n + n = 2 * n(n + 1) ^ 2 = (n + 1) * (n + 1) All goals completed! 🐙 _ = (n + 1) * n + (n + 1) * 1 := n:h:n + n = 2 * n(n + 1) * (n + 1) = (n + 1) * n + (n + 1) * 1 All goals completed! 🐙 _ = n * n + 1 * n + (n + 1) := n:h:n + n = 2 * n(n + 1) * n + (n + 1) * 1 = n * n + 1 * n + (n + 1) All goals completed! 🐙 _ = n^2 + n + (n + 1) := n:h:n + n = 2 * nn * n + 1 * n + (n + 1) = n ^ 2 + n + (n + 1) All goals completed! 🐙 _ = n^2 + (n + n + 1) := n:h:n + n = 2 * nn ^ 2 + n + (n + 1) = n ^ 2 + (n + n + 1) All goals completed! 🐙 _ = n^2 + 2*n + 1 := n:h:n + n = 2 * nn ^ 2 + (n + n + 1) = n ^ 2 + 2 * n + 1 All goals completed! 🐙

The same can be achieved without the calc mode, like this:

example (n : ℕ): (n+1)^2 = n^2 + 2*n + 1 := by
  have h : n + n = 2*n, by { nth_rewrite 0 ← one_mul n,
  nth_rewrite 1 ← one_mul n, rw ← add_mul, },
  rw [pow_two, mul_add, add_mul, mul_one (n+1), one_mul,
  ← pow_two, add_assoc, ← add_assoc n n 1,
  ← add_assoc, ← h],

However, this is much less readable.

Remarks

  • The exact notation is important in calc mode.

  • The calc mode not only works for equalities, but also for inequalities, subset-relations etc.

  • The example above can be solved easily using linarith or ring.

  • In order to generate a proof in calc mode, one can do it as follows (see the example below):

    • give the exact calculation steps without proof (using by sorry)

    • fill in the proofs which are left over.

Here is how to start the proof of the binomial formula. First, leave out all proofs:

declaration uses 'sorry'example (n : ) : n + n = 2*n := n:n + n = 2 * n calc n + n = 1 * n + 1 * n := n:n + n = 1 * n + 1 * n All goals completed! 🐙 _ = (1 + 1) * n := n:1 * n + 1 * n = (1 + 1) * n All goals completed! 🐙 _ = 2 * n := n:(1 + 1) * n = 2 * n All goals completed! 🐙

Then, fill in the details

example (n : ) : n + n = 2*n := n:n + n = 2 * n calc n + n = 1 * n + 1 * n := n:n + n = 1 * n + 1 * n All goals completed! 🐙 _ = (1 + 1) * n := n:1 * n + 1 * n = (1 + 1) * n All goals completed! 🐙 _ = 2 * n := n:(1 + 1) * n = 2 * n All goals completed! 🐙
🔗def
Lean.calc : Lean.ParserDescr

Step-wise reasoning over transitive relations.

calc
  a = b := pab
  b = c := pbc
  ...
  y = z := pyz

proves a = z from the given step-wise proofs. = can be replaced with any relation implementing the typeclass Trans. Instead of repeating the right- hand sides, subsequent left-hand sides can be replaced with _.

calc
  a = b := pab
  _ = c := pbc
  ...
  _ = z := pyz

It is also possible to write the first relation as <lhs>\n _ = <rhs> := <proof>. This is useful for aligning relation symbols, especially on longer: identifiers:

calc abc
  _ = bce := pabce
  _ = cef := pbcef
  ...
  _ = xyz := pwxyz

calc works as a term, as a tactic or as a conv tactic.

See Theorem Proving in Lean 4 for more information.

3.8. cases'🔗

Summary: If a hypothesis is composed, i.e. can be expanded into two or more cases, cases' delivers exactly that. This can be used not only used with hypotheses h : P ∨ Q or h : P ∧ Q, but also with structures that consist of several cases, such as ∃... (here there is a variable and a statement) and x : bool or n : ℕ.

Examples:

Proof state

Tactic

New proof state

h : P ∧ Q

cases h with hP hQ

hP : P
hQ : Q
⊢ R

h : P ∨ Q

cases h with hP hQ

hP : P
⊢ R
hQ : Q
⊢ R

h : False
⊢ P

cases h

no goals🎉

P: ℕ → Prop
h: ∃ (m : ℕ), P m
⊢ Q

cases x with m h1

P : ℕ → Prop
m : ℕ
h1 : P m
⊢ Q

x : Bool
⊢ x = True ∨ x = False

cases x

⊢ False = True ∨ False = False
⊢ True = True ∨ True = False

n : ℕ
⊢ n > 0 → (∃ (k : ℕ), n = k + 1)

cases n

⊢ 0 > 0 → (∃ (k : ℕ), 0 = k + 1)
⊢ n.succ > 0 → (∃ (k : ℕ), n.succ = k + 1)

Remarks:

  • The application cases' n for n : ℕ is strictly weaker than complete induction (see induction). After all, cases only converts n : ℕ into the two cases 0 and succ n, but you cannot use the statement for n-1 to prove the statement for n.

  • A more flexible version of cases' is rcases.

example (P Q : Prop) (hP: P Q) ( hP' : ¬P Q) : Q := P:PropQ:ProphP:PQhP':¬PQQ P:PropQ:ProphP:PQhP':¬PQh:PQP:PropQ:ProphP:PQhP':¬PQh:¬PQ P:PropQ:ProphP:PQhP':¬PQh:PQ All goals completed! 🐙 P:PropQ:ProphP:PQhP':¬PQh:¬PQ All goals completed! 🐙

3.9. change🔗

Summary: Changes the goal (or a hypothesis) into a goal (or a hypothesis) that is defined the same.

Examples:

Proof state

Tactic

New proof state

⊢ : P → false

change ¬P

⊢ ¬P

h : ¬P
⊢ Q

change P → false at h

h: P → false
⊢ Q

xs : x ∈ s
⊢ x ∈ f ⁻¹' (f '' s)

change f x ∈ f '' s

xs : x ∈ s
⊢ f x ∈ f '' s

Remarks:

  • As can be seen from the penultimate example, change also works for hypotheses.

  • Since many tactics test for definitional equality anyway, change is often not necessary. However, it can help to make the proof more readable.

example (P : Prop) (hP : P) (hP' : ¬P) : False := P:ProphP:PhP':¬PFalse P:ProphP:PhP':PFalseFalse P:ProphP:PhP':PFalseP All goals completed! 🐙

A property of, say, the natural numbers, gives rise to Set ℕ by collecting all n : ℕ satisfying the property. In other words, P n and the membership n ∈ {m | P m} are equivalent.

example (P : Prop) (n : ) (hn : P n) : n {m | P m} := P:Propn:hn:P nn{m | P m} P:Propn:hn:P nP n All goals completed! 🐙

3.10. clear🔗

Summary: With clear h the hypothesis h is removed from the goal state (forgotten).

Example

Proof state

Tactic

New proof state

h : P
⊢ Q

clear h

⊢ Q

3.11. congr🔗

Summary: If you have to show an equality f x = f y, then congr uses the statement that the equality is particularly true if x = y.

Examples:

Proof state

Tactic

New proof state

⊢ f x = f y

congr

⊢ x = y

Remarks:

  • The related tactic congr' uses another parameter that determines how many recursive layers are eliminated in the goal; see the examples below.

  • Besides the congr tactic there are several related results which can be applied, e.g. tsum_congr.

Here, congr goes too deep since it tries to match inner arguments:

declaration uses 'sorry'example (f : ) (x y : ) : f (x + y) = f (y + x) := f:x:y:f (x + y) = f (y + x) f:x:y:x = yf:x:y:y = x f:x:y:x = y All goals completed! 🐙 f:x:y:y = x All goals completed! 🐙

We can prevent this by specifying how deep congr shoud go. (The above example is equivalent to congr' 2)

example (f : ) (x y : ) : f (x + y) = f (y + x) := f:x:y:f (x + y) = f (y + x) f:x:y:x + y = y + x All goals completed! 🐙

tsum_congr can be made usefule by using apply or a related tactics.

theorem tsum_congr.{u_1, u_2} : ∀ {α : Type u_1} {β : Type u_2} [inst : AddCommMonoid α] [inst_1 : TopologicalSpace α] {f g : β → α}, (∀ (b : β), f b = g b) → ∑' (b : β), f b = ∑' (b : β), g b := fun {α} {β} [AddCommMonoid α] [TopologicalSpace α] {f g} hfg => congr_arg tsum (funext hfg)#print tsum_congr
theorem tsum_congr.{u_1, u_2} : ∀ {α : Type u_1} {β : Type u_2} [inst : AddCommMonoid α] [inst_1 : TopologicalSpace α]
  {f g : β → α}, (∀ (b : β), f b = g b) → ∑' (b : β), f b = ∑' (b : β), g b :=
fun {α} {β} [AddCommMonoid α] [TopologicalSpace α] {f g} hfg => congr_arg tsum (funext hfg)

3.12. constructor🔗

Summary: If the goal is of the type ⊢ P ∧ Q, it is replaced by constructor into two goals ⊢ P and ⊢ Q.

Examples

Proof state

Tactic

New proof state

⊢ P ∧ Q

constructor

⊢ P
⊢ Q

⊢ P ↔ Q

constructor

⊢ P → Q
⊢ Q → P

Remarks

Note that ⊢ P ↔ Q is identical to ⊢ (P → Q) ∧ (Q → P).

example (P Q R : Prop) (hP : P) (hQ : Q) (hR : R) : P Q R := P:PropQ:PropR:ProphP:PhQ:QhR:RPQR P:PropQ:PropR:ProphP:PhQ:QhR:RPP:PropQ:PropR:ProphP:PhQ:QhR:RQR P:PropQ:PropR:ProphP:PhQ:QhR:RP All goals completed! 🐙 P:PropQ:PropR:ProphP:PhQ:QhR:RQR P:PropQ:PropR:ProphP:PhQ:QhR:RQP:PropQ:PropR:ProphP:PhQ:QhR:RR P:PropQ:PropR:ProphP:PhQ:QhR:RQ All goals completed! 🐙 P:PropQ:PropR:ProphP:PhQ:QhR:RR All goals completed! 🐙

In fact, constructor is the same as refine ⟨?_, ?_⟩ (i.e. leaving two new goals). However, refine is more flexible since it is not bound to two variables:

example (P Q R : Prop) (hP : P) (hQ : Q) (hR : R) : P Q R := P:PropQ:PropR:ProphP:PhQ:QhR:RPQR All goals completed! 🐙

When we have a property of the natural numbers, P : ℕ → Prop, {P // P m} is the subtype which consists of all m for which P m holds. Every (m : {P // P m}) is a pair ⟨m.val, m.prop⟩, where (m.val : ℕ) is its numerical value and m.prop is a proof for P m. So, we can construct a member of the subtype by giving a number n and a proof hn : P n. The constructor subtype_of_mem is the constructor for the subtype {m // P m}. It takes a number n and a proof hn : P n and returns the pair ⟨n, hn⟩.

def mySubtype.mk (P : Prop) (n : ) (hn : P n) : {m // P m} := :Sort ?u.7P:Propn:hn:P n{ m // P m } :Sort ?u.7P:Propn:hn:P nP n:Sort ?u.7P:Propn:hn:P n :Sort ?u.7P:Propn:hn:P nP n All goals completed! 🐙

3.13. exact🔗

Summary: If the goal can be closed with a single command, then it can be closed with the exact tactic. Like many other tactics, exact also works with terms that are definitionally equal.

Examples:

Proof state

Tactic

New proof state

h : P
⊢ P

exact h

no goals

hP: P
hQ: Q ⊢ P ∧ Q

exact ⟨ hP, hQ ⟩

no goals

Remarks:

  • The related tyctics exact? searches for terms which close the goal; see apply?.

  • If the proof consists of a single call of exact, it is easy to translate it to term mode; see easy proofs in term mode.

  • In the third example, note the order in which the two hypotheses hP and hnP are applied. The first hypothesis after exact is always the one whose right side matches the goal. If the goal requires further input, it is appended afterwards.

example (P : Prop) (h : False) : P := P:Proph:FalseP All goals completed! 🐙

3.14. exfalso🔗

Summary: The statement false → P is true for all P. If the current goal is ⊢ P, and you would apply this true statement using apply, the new goal would be ⊢ false. This is exactly what the exfalso tactic does.

Examples:

Proof state

Tactic

New proof state

h : P
⊢ Q

exfalso

h : P
⊢ False

hP : P
hnP : ¬P
⊢ Q

exfalso

hP : P
hnP: ¬P
⊢ false

Remarks:

  • If you use this tactic, you leave the realm of constructive mathematics. (This dispenses with the rule of the excluded middle.)

  • exfalso is the same as apply False.elim; see the examples for exact.

example (P : Prop) : False P := P:PropFalseP All goals completed! 🐙

3.15. have🔗

Summary: By using have we introduce a new goal, which we have to prove first. Afterwards, it is available as a hypothesis in all further goals. This is identical to first proving a lemma h with the statement after have h : and then reusing it at the appropriate place in the proof (for example with apply or rw).

Examples:

Proof state

Tactic

New proof state

⊢ R

have h : P ↔ Q

⊢ P ↔ Q
h : P ↔ Q
⊢ R

⊢ P

have h1 : ∃ (m : ℕ), f 27 m, ...
cases h1 with m hm

m : ℕ
hm: f 27 m
⊢ P

Remarks:

  • Assume you want to use have. You could as well formulate a separate lemma and use it afterwards. It is not always clear which is better.

  • If the proof of the statement is short and is only used once in your proof, you might want to consider replacing its proof in the place where it is needed.

  • Suppose we have two goals (let's call them ⊢ 1 and ⊢ 2), and we need the statement of ⊢ 1 in the proof of ⊢ 2. We can first introduce a third goal with have h := ⊢ 1 (where ⊢ 1 is to be replaced by the statement). Then ⊢ 1 can be proved with exact, and has the statement ⊢ 1 available in the proof of ⊢ 2.

example (x : ) (d : ): 0 (d : ) * x^2 := x:d:0d * x ^ 2 have h : d 0 := x:d:0d * x ^ 2 All goals completed! 🐙 have h1 : (0 : ) = d * 0 := x:d:0d * x ^ 2 All goals completed! 🐙 x:d:h:d0h1:0 = d * 0d * 0d * x ^ 2 x:d:h:d0h1:0 = d * 00x ^ 2x:d:h:d0h1:0 = d * 00d x:d:h:d0h1:0 = d * 00d All goals completed! 🐙
example (P : Prop) : False → P := by
  exact False.elim

3.16. induction🔗

Summary:

Inductive types allow the possibility of proving statements about them by means of induction. This includes, for example, the usual case of complete induction over natural numbers.

Examples

Proof state

Tactic

New proof state

n : ℕ
⊢ n = 0 + n

induction n with
| zero ↦ ?_
| succ n hn ↦ ?_

⊢ 0 = 0 + 0
hd : d = 0 + d
⊢ d.succ = 0 + d.succ

example (n : ) : n = 0 + n := n:n = 0 + n induction n with | zero => All goals completed! 🐙 | succ n hn => All goals completed! 🐙

3.17. intro🔗

Summary

If the goal is of the form ⊢ P → Q or ∀ (n : ℕ), P n, you can proceed with intro P or intro n. You can use several intro commands at the same time to summarize a single one. A little more precisely, intro h1 h2 h3, is identical to intro h1; intro h2; intro h3.

Examples

Proof state

Tactic

New proof state

⊢ P → Q

intro hP

hP : P
⊢ Q

f : α → Prop
⊢ ∀ (x : α), f x

intro x

f: α → Prop
x : α
⊢ f x

⊢ P → Q → R

intro hP hQ

hP : P
hQ : Q
⊢ R

P : ℕ → Prop
⊢ ∀ (n : ℕ), P n → Q

intro n hP

P : ℕ → Prop
n : ℕ
hP: P n ⊢ Q

example (P : Prop) : P P := P:PropPP P:Proph:PP All goals completed! 🐙 example (P : Prop) : P P P P := P:PropPPPP P:Proph₁:Ph₂:Ph₃:PP All goals completed! 🐙 example (P Q : Prop) : (P Q) P Q := P:PropQ:Prop(PQ) → PQ P:PropQ:Proph₁:PQPQ All goals completed! 🐙

Remarks

  • Several intro commands in a row are best combined. Furthermore, rintro is a more flexible variant.

  • A reversal of intro is revert.

3.18. left🔗

Summary:

The application of left, is identical to apply h for h : P → P ∨ Q. So if you have a goal of the form ⊢ P ∨ Q, left, causes you to have only the goal ⊢ P. After all, it is sufficient to show P to close the goal.

Examples:

Proof state

Tactic

New proof state

⊢ P ∨ Q

left

⊢ P

⊢ ℕ

left

no goals🎉

The second example requires a little explanation. First of all, you have to understand that the goal ⊢ ℕ is to show that there is a term of type , i.e. that there is a natural number. Now you have to know how is implemented in Lean. This is

inductive nat | zero : nat | succ (n : nat) : nat

together with

notation `ℕ` := nat

This means: The type is defined by the fact that zero is a term of this type, and that there is a function succ : ℕ → ℕ. Thus, in the second example, the input left, is closed because by definition zero : ℕ holds, so in particular there is a term of type .

example (P : Prop) : False P := P:PropFalseP All goals completed! 🐙

Remarks:

  • See also right, for the equivalent tactic, which is apply h for h : Q → P ∨ Q.

  • As in the second example, left, can always be applied when dealing with an inductive type with two constructors (such like ).

3.19. linarith🔗

Summary: This tactic can prove equations and inequalities with the help of hypotheses. It is important that the hypotheses used are also only equations and inequalities. So here we are working mainly with the transitivity of < together with arithmetic rules.

Examples:

Proof state

Tactic

New proof state

h₁ : a < b
h₂ : b < c
⊢ a < c

linarith

no goals

example (P : Prop) : False P := P:PropFalseP All goals completed! 🐙

3.20. norm_num🔗

Summary:

As long as there are no variables, norm_num can do calculations which involve =, <, or .

Examples:

Proof state

Tactic

New proof state

⊢ 2 + 2 < 5

norm_num

no goals

⊢ | (1 : ℝ) | = 1

norm_num

no goals

example (P : Prop) : False P := P:PropFalseP All goals completed! 🐙

Remarks

norm_num knows about some more operations, e.g. absolute values; see also the second example.

3.21. nth_rewrite🔗

Summary:

This tactic is related to rw. The difference is that you can specify the occurrence number of the term to be replaced on which rw is to be applied. The exact syntax is nth_rewrite k h, where k is the number (starting with $0$) of the term to be replaced and h is the hypothesis to be replaced. As with rw, this must be in the form h : x=y or h : A↔B.

Examples:

Proof state

Tactic

New proof state

n : ℕ
⊢ 0 + n = 0 + 0 + n

nth_rewrite 0 zero_add

n : ℕ
⊢ n = 0 + 0 + n

n : ℕ
⊢ 0 + n = 0 + 0 + n

nth_rewrite 1 zero_add

n : ℕ
⊢ 0 + n = 0 + n

n : ℕ
⊢ 0 + n = 0 + 0 + n

nth_rewrite 2 zero_add

n : ℕ
⊢ 0 + n = 0 + n

In the above example, Lean sees three terms of the form 0 + ?_: Number 0 is on the left-hand side, for numbers 1 and 2, on the right side (because of the parenthesis 0 + 0 + n = (0 + 0) + n), the second = is checked first. To the left of it is 0 + 0, which is by definition identical to 0. applying rw zero_add here, the term is converted to n. For number 2, you see 0 + 0, determine that it is of the desired form and convert it to 0.

example (P : Prop) : False P := P:PropFalseP All goals completed! 🐙

3.22. obtain🔗

Summary: The obtain tactic can be used to merge have and cases into one command.

Examples:

Proof state

Tactic

New proof state

f : ℕ → ℕ → Prop
h : ∀ (n : ℕ), ∃ (m : ℕ), f n m

obtain ⟨ m, hm ⟩ := h 27

f: ℕ → ℕ → Prop
h : ∀ (n : ℕ), ∃ (m : ℕ), f n m
m : ℕ
hm : f 27 m

example (P : Prop) : False P := P:PropFalseP All goals completed! 🐙

3.23. push_neg🔗

Summary: In many steps of a proof, a negation must be carried out. In order to process the corresponding quantifiers etc. as well and to better reusable, the tactic push_neg is available.

Examples

Proof state

Tactic

New proof state

⊢ ¬(P ∨ Q)

push_neg

⊢ ¬P ∧ ¬Q

h : ¬(P ∨ Q)

push_neg at h

h : ¬P ∧ ¬Q

⊢ ¬(P ∧ Q)

push_neg

⊢ P → ¬Q

P : X → Prop
⊢ ¬∀ (x : X), P x

push_neg

P : X → Prop
⊢ ∃ (x : X), ¬P x

P : X → Prop
⊢ ¬∃ (x : X), P x

push_neg

P : X → Prop
⊢ ∀ (x : X), ¬P x

example (P : Prop) : False P := P:PropFalseP All goals completed! 🐙

Notes:

This tactic also works with other objects, such as sets.

3.24. rcases🔗

Proof state

Tactic

New proof state

h : P ∧ Q ∨ P ∧ R
⊢ P

rcases h with (⟨hP1,hQ⟩|⟨hP2,hR⟩)

hP1 : P
hQ : Q
⊢ P
hP2 : P
hR : R
⊢ P

Summary: rcases is a more flexible version of cases. Here, it is allowed to use ⟨ hP, hQ ⟩ (or (hP | hQ)) to to split the hypotheses hP and hQ into their cases. As can be seen in the example above, it is also possible to nest ⟨.,.⟩ and (.|.).

Examples:

Proof state

Tactic

New proof state

h : P ∧ Q
⊢ R

rcases h with⟨ hP, hQ ⟩ + hP : P {br}[]hQ : Q {br}[]⊢ R * + h : P ∨ Q{br}[]⊢ R + rcases h with( hP | hQ )

hP : P
⊢ R
hQ : Q
⊢ R

h : ∃ (m : ℕ) (hg : 0 ≤ m), m < n
⊢ P

rcases h with⟨m, h1, h2⟩

n m : ℕ
h1 : 0 ≤ m
h2 : m < n
⊢ 1 < n

Remarks:

The last example shows how to use rcases to directly resolve a ∃ quantifier in a hypothesis that has more than one constraint (here: 0 ≤ m) and m < n can be resolved directly.

example (P : Prop) : False P := P:PropFalseP All goals completed! 🐙
🔗inductive type
Lean.Elab.Tactic.RCases.RCasesPatt : Type

An rcases pattern can be one of the following, in a nested combination:

  • A name like foo

  • The special keyword rfl (for pattern matching on equality using subst)

  • A hyphen -, which clears the active hypothesis and any dependents.

  • A type ascription like pat : ty (parentheses are optional)

  • A tuple constructor like p1, p2, p3

  • An alternation / variant pattern p1 | p2 | p3

Parentheses can be used for grouping; alternation is higher precedence than type ascription, so p1 | p2 | p3 : ty means (p1 | p2 | p3) : ty.

N-ary alternations are treated as a group, so p1 | p2 | p3 is not the same as p1 | (p2 | p3), and similarly for tuples. However, note that an n-ary alternation or tuple can match an n-ary conjunction or disjunction, because if the number of patterns exceeds the number of constructors in the type being destructed, the extra patterns will match on the last element, meaning that p1 | p2 | p3 will act like p1 | (p2 | p3) when matching a1 a2 a3. If matching against a type with 3 constructors, p1 | (p2 | p3) will act like p1 | (p2 | p3) | _ instead.

Constructors

paren (ref : Lean.Syntax)
    : Lean.Elab.Tactic.RCases.RCasesPattLean.Elab.Tactic.RCases.RCasesPatt

A parenthesized expression, used for hovers

one (ref : Lean.Syntax)
    : Lean.NameLean.Elab.Tactic.RCases.RCasesPatt

A named pattern like foo

clear (ref : Lean.Syntax)
    : Lean.Elab.Tactic.RCases.RCasesPatt

A hyphen -, which clears the active hypothesis and any dependents.

explicit (ref : Lean.Syntax)
    : Lean.Elab.Tactic.RCases.RCasesPattLean.Elab.Tactic.RCases.RCasesPatt

An explicit pattern @pat.

typed (ref : Lean.Syntax)
    : Lean.Elab.Tactic.RCases.RCasesPattLean.TermLean.Elab.Tactic.RCases.RCasesPatt

A type ascription like pat : ty (parentheses are optional)

tuple (ref : Lean.Syntax)
    : List Lean.Elab.Tactic.RCases.RCasesPattLean.Elab.Tactic.RCases.RCasesPatt

A tuple constructor like p1, p2, p3

alts (ref : Lean.Syntax)
    : List Lean.Elab.Tactic.RCases.RCasesPattLean.Elab.Tactic.RCases.RCasesPatt

An alternation / variant pattern p1 | p2 | p3

3.25. refine🔗

Summary: The refine tactic is like exact with holes. More precisely: if the goal is to apply a combination of hypotheses, you can do that with 'refine' and write an open term '' for each. You then get each '' back as a new goal (where those with definitional equality are solved immediately).

Examples:

Proof state

Tactic

New proof state

hQ : Q
⊢ P ∧ Q

refine ⟨?_, hQ⟩

hQ : Q
⊢ P

⊢ ∃ (n : ℕ) (h : n > 0), n ^ 2 = 9

refine ⟨3, , by normnum⟩

⊢ 3 > 0

example (P : Prop) : False P := P:PropFalseP All goals completed! 🐙

3.26. revert🔗

Summary: revert is the opposite of intro: It takes a hypothesis from the local context and inserts it as a precondition into the goal.

Examples

Proof state

Tactic

New proof state

h : P
⊢ Q

revert hP

⊢ P → Q

example (P : Prop) : False P := P:PropFalseP All goals completed! 🐙

Remarks:

revert is rarely needed; actually only when you want to apply an already proven result exactly and first want to establish the correct form of the goal.

3.27. rfl🔗

Summary: This tactic proves the equality (or equivalence) of two definitionally equal terms.

Examples:

Proof state

Tactic

New proof state

⊢ P ↔ P oder
⊢ P = P

rfl

no goals

⊢ 1 + 2 = 3

rfl

no goals

Remarks:

The second example works because both sides are by definition equal to succ succ succ 0.

example (P : Prop) : False P := P:PropFalseP All goals completed! 🐙
🔗def
Lean.Elab.Tactic.Rfl.evalApplyRfl
    : Lean.Elab.Tactic.Tactic

This tactic applies to a goal whose target has the form x ~ x, where ~ is a reflexive relation, that is, a relation which has a reflexive lemma tagged with the attribute [refl].

Summary: See left, where the adjustments are obvious.

Examples

Proof state

Tactic

New proof state

h : P ∨ Q

right

⊢ Q

example (P : Prop) : False P := P:PropFalseP All goals completed! 🐙

3.29. ring🔗

Summary: The ring uses rules of calculation such as associativity, commutativity, and distributivity to achieve the goal.

Examples

Proof state

Tactic

New proof state

x y : ℝ
⊢ x + y = y + x

ring

no goals

n : ℕ
⊢ (n + 1)^2 = n^2 + 2*n + 1

ring

no goals

example (P : Prop) : False P := P:PropFalseP All goals completed! 🐙

Remarks:

  • The second example works even though is not a ring (but only a half-ring). It would also work with n : ℝ (since has more calculation rules than ).

  • ring is only used to close the goal.

3.30. rintro🔗

Summary: The rintro tactic is used to process several intro and cases tactics on one line.

Examples

Proof state

Tactic

New proof state

⊢ P ∨ Q → R

rintro ( hP | hQ )
=
intro h
cases h with hP hQ

hP : P
⊢ P
hQ : Q
⊢ Q

⊢ P ∧ Q → R

rintro ⟨ hP , hQ ⟩
=
intro h
cases h with h1 h2

hP : P
hQ : Q
⊢ R

Notes:

Here, more than two can also be split into cases in one step: With A ∨ B ∨ C, rintro (A | B | C) introduces three goals.

example (P : Prop) : False P := P:PropFalseP All goals completed! 🐙
🔗def
Lean.Elab.Tactic.RCases.rintro
    (pats : Lean.TSyntaxArray `rintroPat)
    (ty? : Option Lean.Term) (g : Lean.MVarId)
    : Lean.Elab.TermElabM (List Lean.MVarId)

The implementation of the rintro tactic. It takes a list of patterns pats and an optional type ascription ty? and introduces the patterns, resulting in zero or more goals.

3.31. rw🔗

Summary: rw stands for rewrite. For rw h to work, h must be an expression of the type h : x=y or h : A↔B. In this case, rw h replaces every term that is syntactically identical to x (or A) is replaced by y (or B). This also works if h is an already proven result (i.e. a lemma or theorem). With rw ← h is applied from right to left. (In the above example, y is replaced by x and B by A.)

Examples

Proof state

Tactic

New proof state

h : P ↔ Q
⊢ P

rw [h]

h : P ↔ Q
⊢ Q

h : P ↔ Q
⊢ Q

rw [← h]

h : P ↔ Q
⊢ P

h : P ↔ Q
hP : P

rw [h] at hP

h : P ↔ Q
hP : Q

h : P ↔ Q
hQ : Q

rw [← h] at hQ

h : P ↔ Q
hQ : P

k m: ℕ
⊢ k + m + 0 = m + k + 0

rw [add_comm]

k m : ℕ
⊢ 0 + (k + m) = m + k + 0

k m : ℕ
⊢ k + m + 0 = m + k + 0

rw [add_comm k m]

⊢ m + k + 0 = m + k + 0

k m : ℕ
⊢ k + m + 0 = m + k + 0

rw [← add_comm k m]

⊢ k + m + 0 = k + m + 0

k m : ℕ
⊢ k + m + 0 = m + k + 0

rw [add_zero, add_zero]

k m : ℕ
⊢ k + m = m + k

For the last four examples, you first need to know that addcomm and addzero are the statements

lemma add_comm : ∀ {G : Type} [_inst_1 : add_comm_semigroup G] (a b : G),
a + b = b + a
add_zero : ∀ {M : Type} [_inst_1 : add_zero_class M] (a : M), a + 0 = a

In the first of the four examples, rw applies to the first occurrence of a term of type a + b. Due to the internal bracketing, (k + m) + 0 is on the left side, so that the rw results in a 0 + k + m. If you want to use the commutativity in the term k + m, you need the second (or third) example, where rw add_comm k m leads to the desired progress. In the last example, the two + 0 terms are first eliminated by rw add_zero.

Notes

  • rw is used very often in practice to apply statements from the mathlib (at least if they are of the type = or ).

  • If you want to combine several rw commands, you can do so in square brackets, for example rw [h1, h2] or rw [h1, ←h2, h3].

  • rw immediately executes a refl after its application. This leads to the second and third examples of the applications of add_comm and add_zero that the new proof state is not as specified, but no goals.

  • If you do not want to perform a rw in sequence (as in the double elimination of the +0 above), you can use nth_rewrite to rewrite the second occurrence of a term.

  • The rw tactic does not work when it comes after a binder, which could be a ∀ ∃ ∑. In this case, simp_rw will hopefully help.

example (P : Prop) : False P := P:PropFalseP All goals completed! 🐙

3.32. simp🔗

Summary: In mathlib there are many lemmas with = or statements that can be applied with rw and are marked with @[simp]. These marked lemmas have the property that the right side is a simplified form of the left side. With simp, lean looks for matching lemmas and tries to apply them.

Examples

Proof state

Tactic

New proof state

⊢ n + 0 = n

simp

no goals 🎉

h : n + 0 = m
⊢ P

simp at h

h : n = m
⊢ P

Remarks:

If you want to know which lemmas were used, try simp?. This provides some clues.

Proof state

Tactic

New proof state

⊢ n + 0 = n

simp?

no goals 🎉
Try this:
simp only add_zero, eq_self_iff_true

example (P : Prop) : False P := P:PropFalseP All goals completed! 🐙

3.33. specialize🔗

Proof state

Tactic

New proof state

f : ℕ → Prop
h : ∀ (n : ℕ), f n
⊢ P

specialize h 13

f: ℕ → Prop
h : f 13
⊢ P

Summary: In a hypothesis h : ∀ n, ..., ... applies to all n, but for the proof of the goal, you may only need a specific n. If you specify specialize h followed by the value for which h is needed, the hypothesis changes accordingly.

Examples

example (p : Prop) (hp : (n : ), p n) : (p 0) := p:Prophp:∀ (n : ), p np 0 p:Prophp:p 0p 0 All goals completed! 🐙

Remarks

  • Just as with use, you have to be careful that the goal remains provable.

  • If you want to use two values of the hypothesis h, then let h' := h first provides a duplication of the hypothesis, so that you can then apply specialize to h and h'.

example (h : (n : ), 0 n) : 0 0 := h:∀ (n : ), 0n00 h:0000 All goals completed! 🐙 example (h : (ε : ) ( : 0 < ε), (n : ), 1 < n ε) := h:∀ (ε : ), 0 < ε → ∃ n, 1 < nε0 < 0 → ∃ n, 1 < n0 h✝:∀ (ε : ), 0 < ε → ∃ n, 1 < nεh:0 < 0 → ∃ n, 1 < n00 < 0 → ∃ n, 1 < n0 All goals completed! 🐙

3.34. tauto🔗

Summary: tauto solves all goals that can be solved with a truth table.

Examples

Proof state

Tactic

New proof state

⊢ P ∧ Q → P

tauto oder tauto!

No goals

⊢ ((P → Q) → P) → P

tauto!

No goals

The truth tables for ¬P, P ∧ Q and P ∨ Q are as follows; if more terms of type Prop are involved, there are more lines.

P

¬P

true

false

false

true

P

Q

(P ∧ Q)

true

true

true

false

true

false

true

false

false

false

false

false

P

Q

(P ∨ Q)

true

true

true

false

true

true

true

false

true

false

false

false

Notes

The difference between tauto and tauto! is that in the latter tactic, the rule of the excluded middle is allowed. The second example can therefore only be solved with tauto!, but not with tauto.

example (P : Prop) : False P := P:PropFalseP All goals completed! 🐙

3.35. triv🔗

Summary

triv solves an objective that is, by definition, identical to true. It also solves objectives that can be solved with refl .

Examples

Proof state

Tactic

New proof state

⊢ True

triv

No goals

⊢ x = x

triv

No goals

example (P : Prop) : False P := P:PropFalseP All goals completed! 🐙

3.36. use🔗

Proof state

Tactic

New proof state

f : α → Prop
y : α
∃ (x : α), f x

use y

f : α → Prop
y : α
f y

Summary: The use tactic is used for goals that begin with . Here, parameters are used to indicate which object quantified by is to be reused in the proof.

Examples

Proof state

Tactic

New proof state

⊢ ∃ (k : ℕ), k * k = 16

use 4

⊢ 4 * 4 = 16

⊢ ∃ (k l : ℕ), k * l = 16

use 8, 2

⊢ 8 * 2 = 16

In this example, Lean knows that 4 * 4 = 16 by rfl, so we need not type it.

example : (k : ), k * k = 16 := k, k * k = 16 All goals completed! 🐙

In various cases, refine can be used instead of use, e.g. here:

example : (k : ), k * k = 16 := k, k * k = 16 refine 4, 4 * 4 = 16 All goals completed! 🐙

Sometimes, one not only needs to give a term (e.g. δ : ℝ), but also a property (e.g. hδ : 0 < δ). This means we have to give two terms:

example (ε : ) ( : 0 < ε) : (δ : ) ( : 0 < δ), δ < ε := ε::0 < εδ, ∃ (_ : 0 < δ), δ < ε ε::0 < ε∃ (_ : 0 < ε / 2), ε / 2 < ε ε::0 < εε / 2 < ε All goals completed! 🐙

This can also be written as follows:

example (ε : ) ( : 0 < ε) : (δ : ) ( : 0 < δ), δ < ε := ε::0 < εδ, ∃ (_ : 0 < δ), δ < ε ε::0 < εε / 2 < ε All goals completed! 🐙

Again, refine is an abbreviation for the whole proof. Note that we have to provide a triple, cosisting of δ, a proof of 0 < δ, and a proof of δ < ε:

example (ε : ) ( : 0 < ε) : (δ : ) ( : 0 < δ), δ < ε := ε::0 < εδ, ∃ (_ : 0 < δ), δ < ε All goals completed! 🐙