applye 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.
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 + nAll goals completed! 🐙
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 showtbyassumption.
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:
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.
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.
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.
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 + 1haveh:n+n=2*n:=n:ℕ⊢ (n + 1) ^ 2 = n ^ 2 + 2 * n + 1nth_rewrite1[n:ℕ⊢ 1 * n + n = 2 * n]nth_rewrite2[n:ℕ⊢ 1 * n + 1 * n = 2 * n]n:ℕ⊢ (1 + 1) * n = 2 * nAll 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) * 1All 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 * n⊢ n * n + 1 * n + (n + 1) = n ^ 2 + n + (n + 1)All goals completed! 🐙_=n^2+(n+n+1):=n:ℕh:n + n = 2 * n⊢ n ^ 2 + n + (n + 1) = n ^ 2 + (n + n + 1)All goals completed! 🐙_=n^2+2*n+1:=n:ℕh:n + n = 2 * n⊢ n ^ 2 + (n + n + 1) = n ^ 2 + 2 * n + 1All 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 * ncalcn+n=1*n+1*n:=n:ℕ⊢ n + n = 1 * n + 1 * nAll goals completed! 🐙_=(1+1)*n:=n:ℕ⊢ 1 * n + 1 * n = (1 + 1) * nAll goals completed! 🐙_=2*n:=n:ℕ⊢ (1 + 1) * n = 2 * nAll goals completed! 🐙
Then, fill in the details
example(n:ℕ):n+n=2*n:=n:ℕ⊢ n + n = 2 * ncalcn+n=1*n+1*n:=n:ℕ⊢ n + n = 1 * n + 1 * nAll goals completed! 🐙_=(1+1)*n:=n:ℕ⊢ 1 * n + 1 * n = (1 + 1) * nAll goals completed! 🐙_=2*n:=n:ℕ⊢ (1 + 1) * n = 2 * nAll goals completed! 🐙
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:
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 : ℕ.
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 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.
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(PQR:Prop)(hP:P)(hQ:Q)(hR:R):P∧Q∧R:=P:PropQ:PropR:ProphP:PhQ:QhR:R⊢ P ∧ Q ∧ RAll 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⟩.
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.
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.
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.
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.
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:Prop⊢ P → PP:Proph:P⊢ PAll goals completed! 🐙example(P:Prop):P→P→P→P:=P:Prop⊢ P → P → P → PP:Proph₁:Ph₂:Ph₃:P⊢ PAll goals completed! 🐙example(PQ:Prop):(P→Q)→P→Q:=P:PropQ:Prop⊢ (P → Q) → P → QP:PropQ:Proph₁:P → Q⊢ P → QAll goals completed! 🐙
Remarks
Several intro commands in a row are best combined. Furthermore, rintro is a more flexible variant.
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
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 ℕ.
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.
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.
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.
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.
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.
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).
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.
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].
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 ℕ).
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.
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.
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
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:ℕ),pn):(p0):=p:ℕ → Prophp:∀ (n : ℕ), pn⊢ p 0p:ℕ → Prophp:p 0⊢ p 0All 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 : ℕ), 0 ≤ n⊢ 0 ≤ 0h:0 ≤ 0⊢ 0 ≤ 0All goals completed! 🐙example(h:∀(ε:ℝ)(hε:0<ε),∃(n:ℕ),1<n•ε):=h:∀ (ε : ℝ), 0 < ε → ∃ n, 1 < n • ε⊢ 0 < 0 → ∃ n, 1 < n • 0h✝:∀ (ε : ℝ), 0 < ε → ∃ n, 1 < n • εh:0 < 0 → ∃ n, 1 < n • 0⊢ 0 < 0 → ∃ n, 1 < n • 0All goals completed! 🐙
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.
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 = 16All goals completed! 🐙
In various cases, refine can be used instead of use, e.g. here:
example:∃(k:ℕ),k*k=16:=⊢ ∃ k, k * k = 16refine⟨4,⊢ 4 * 4 = 16All 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: