This tactic applies to a goal whose target has the form x ~ x,
where ~ is equality, heterogeneous equality or any relation that
has a reflexivity lemma tagged with the attribute @[refl].
1.9. Basic tactics / assumptions
rflsymm-
symmapplies to a goal whose target has the formt ~ uwhere~is a symmetric relation, that is, a relation which has a symmetry lemma tagged with the attribute [symm]. It replaces the target withu ~ t. -
symm at hwill rewrite a hypothesish : t ~ utoh : u ~ t.
trans
trans applies to a goal whose target has the form t ~ u where ~ is a transitive relation,
that is, a relation which has a transitivity lemma tagged with the attribute [trans].
-
trans sreplaces the goal with the two subgoalst ~ sands ~ u. -
If
sis omitted, then a metavariable is used instead.
Additionally, trans also applies to a goal whose target has the form t → u,
in which case it replaces the goal with t → s and s → u.
assumption
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.
exact
exact e closes the main goal if its target type matches that of e.
apply
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.
Note that apply and apply at are formally considered two distinct tactics
even though they appear to be one to the user.
apply at
apply t at i will use forward reasoning with t at the hypothesis i.
Explicitly, if t : α₁ → ⋯ → αᵢ → ⋯ → αₙ and i has type αᵢ, then this tactic will add
metavariables/goals for any terms of αⱼ for j = 1, …, i-1,
then replace the type of i with αᵢ₊₁ → ⋯ → αₙ by applying those metavariables and the
original i.
specialize
The tactic specialize h a₁ ... aₙ works on local hypothesis h.
The premises of this hypothesis, either universal quantifications or
non-dependent implications, are instantiated by concrete terms coming
from arguments a₁ ... aₙ.
The tactic adds a new hypothesis with the same name h := h a₁ ... aₙ
and tries to clear the previous one.
generalize-
generalize ([h :] e = x),+replaces all occurrenceses in the main goal with a fresh hypothesisxs. Ifhis given,h : e = xis introduced as well. -
generalize e = x at h₁ ... hₙalso generalizes occurrences ofeinsideh₁, ...,hₙ. -
generalize e = x at *will generalize occurrences ofeeverywhere.
have
The have tactic is for adding hypotheses to the local context of the main goal.
-
have h : t := eadds the hypothesish : tifeis a term of typet. -
have h := euses the type ofefort. -
have : t := eandhave := eusethisfor the name of the hypothesis. -
have pat := efor a patternpatis equivalent tomatch e with | pat => _, where_stands for the tactics that follow this one. It is convenient for types that have only one applicable constructor. For example, givenh : p ∧ q ∧ r,have ⟨h₁, h₂, h₃⟩ := hproduces the hypothesesh₁ : p,h₂ : q, andh₃ : r. -
The syntax
have (eq := h) pat := eis equivalent tomatch h : e with | pat => _, which adds the equationh : e = patto the local context.
The tactic supports all the same syntax variants and options as the have term.
let
The let tactic is for adding definitions to the local context of the main goal.
-
let x : t := eadds the definitionx : t := eifeis a term of typet. -
let x := euses the type ofefort. -
let : t := eandlet := eusethisfor the name of the hypothesis. -
let pat := efor a patternpatis equivalent tomatch e with | pat => _, where_stands for the tactics that follow this one. It is convenient for types that let only one applicable constructor. For example, givenp : α × β × γ,let ⟨x, y, z⟩ := pproduces the local variablesx : α,y : β, andz : γ. -
The syntax
let (eq := h) pat := eis equivalent tomatch h : e with | pat => _, which adds the equationh : e = patto the local context.
The tactic supports all the same syntax variants and options as the let term.
set
replace
Acts like have, but removes a hypothesis with the same name as
this one if possible. For example, if the state is:
f : α → β h : α ⊢ goal
Then after replace h := f h the state will be:
f : α → β h : β ⊢ goal
whereas have h := f h would result in:
f : α → β h† : α h : β ⊢ goal
This can be used to simulate the specialize and apply at tactics of Coq.
suffices
Given a main goal ctx ⊢ t, suffices h : t' from e replaces the main goal with ctx ⊢ t',
e must have type t in the context ctx, h : t'.
The variant suffices h : t' by tac is a shorthand for suffices h : t' from by tac.
If h : is omitted, the name this is used.
obtain
The obtain tactic is a combination of have and rcases. See rcases for
a description of supported patterns.
obtain ⟨patt⟩ : type := proof
is equivalent to
have h : type := proof rcases h with ⟨patt⟩
If ⟨patt⟩ is omitted, rcases will try to infer the pattern.
If type is omitted, := proof is required.
refine
refine e behaves like exact e, except that named (?x) or unnamed (?_)
holes in e that are not solved by unification with the main goal's target type
are converted into new goals, using the hole's name, if any, as the goal case name.
convert
The exact e and refine e tactics require a term e whose type is
definitionally equal to the goal. convert e is similar to refine e,
but the type of e is not required to exactly match the
goal. Instead, new goals are created for differences between the type
of e and the goal using the same strategies as the congr! tactic.
For example, in the proof state
n : ℕ, e : Prime (2 * n + 1) ⊢ Prime (n + n + 1)
the tactic convert e using 2 will change the goal to
⊢ n + n = 2 * n
In this example, the new goal can be solved using ring.
The using 2 indicates it should iterate the congruence algorithm up to two times,
where convert e would use an unrestricted number of iterations and lead to two
impossible goals: ⊢ HAdd.hAdd = HMul.hMul and ⊢ n = 2.
A variant configuration is convert (config := .unfoldSameFun) e, which only equates function
applications for the same function (while doing so at the higher default transparency).
This gives the same goal of ⊢ n + n = 2 * n without needing using 2.
The convert tactic applies congruence lemmas eagerly before reducing,
therefore it can fail in cases where exact succeeds:
def p (n : ℕ) := True example (h : p 0) : p 1 := by exact h -- succeeds example (h : p 0) : p 1 := by convert h -- fails, with leftover goal `1 = 0`
Limiting the depth of recursion can help with this. For example, convert h using 1 will work
in this case.
The syntax convert ← e will reverse the direction of the new goals
(producing ⊢ 2 * n = n + n in this example).
Internally, convert e works by creating a new goal asserting that
the goal equals the type of e, then simplifying it using
congr!. The syntax convert e using n can be used to control the
depth of matching (like congr! n). In the example, convert e using 1
would produce a new goal ⊢ n + n + 1 = 2 * n + 1.
Refer to the congr! tactic to understand the congruence operations. One of its many
features is that if x y : t and an instance Subsingleton t is in scope,
then any goals of the form x = y are solved automatically.
Like congr!, convert takes an optional with clause of rintro patterns,
for example convert e using n with x y z.
The convert tactic also takes a configuration option, for example
convert (config := {transparency := .default}) h
These are passed to congr!. See Congr!.Config for options.
constructor
If the main goal's target type is an inductive type, constructor solves it with
the first matching constructor, or else fails.
injection
The injection tactic is based on the fact that constructors of inductive data
types are injections.
That means that if c is a constructor of an inductive datatype, and if (c t₁)
and (c t₂) are two terms that are equal then t₁ and t₂ are equal too.
If q is a proof of a statement of conclusion t₁ = t₂, then injection applies
injectivity to derive the equality of all arguments of t₁ and t₂ placed in
the same positions. For example, from (a::b) = (c::d) we derive a=c and b=d.
To use this tactic t₁ and t₂ should be constructor applications of the same constructor.
Given h : a::b = c::d, the tactic injection h adds two new hypothesis with types
a = c and b = d to the main goal.
The tactic injection h with h₁ h₂ uses the names h₁ and h₂ to name the new hypotheses.
intro
Introduces one or more hypotheses, optionally naming and/or pattern-matching them.
For each hypothesis to be introduced, the remaining main goal's target type must
be a let or function type.
-
introby itself introduces one anonymous hypothesis, which can be accessed by e.g.assumption. -
intro x yintroduces two hypotheses and names them. Individual hypotheses can be anonymized via_, or matched against a pattern:intro (a, b) -- ..., a : α, b : β ⊢ ... -
Alternatively,
introcan be combined with pattern matching much likefun:intro | n + 1, 0 => tac | ...
revert
revert x... is the inverse of intro x...: it moves the given hypotheses
into the main goal's target type.