Guide: Conversion mode tactic #
This is a curated guide to point you toward how conv mode works and what tactics are available.
It is not meant to be comprehensive, but rather a "cheat sheet." See also the
conv introduction.
Syntax #
The syntax for the conv tactic is
"conv" ("at" ident)? ("in" ("(occs :=" ("*" <|> num+) ")")? term)? "=>" convSeq
where convSeq is any sequence of "conv tactics", which are tactics specifically written
for conv mode.
The in clause is exactly the same as the arguments to the conv tactic pattern.
conv in ...pattArgs... =>
...
is short for
conv =>
pattern ...patArgs...
...
Note that conv in (occs := 1 2 3) pat => ... starts with three goals (one for each occurrence),
but conv in (occs := *) pat => ... starts with a single goal that converts in all occurrences
simultaneously.
Mathlib also provides conv_lhs and conv_rhs variants to immediately apply either the
lhs or rhs tactic.
What is conv mode? #
conv mode is essentially the normal tactic mode but with two differences.
Only "
convtactics" can appear in theconvblock. These are tactics with syntax in theconvcategory.The goals are all of the form
⊢ lhs = ?rhswith?rhsa metavariable, but the goals are annotated in such a way that they display as| lhs.
Each conv tactic is aware that the goal is of this form, and in addition to solving for the
goal like normal, they also solve for the ?rhs metavariable in some controlled way.
For example, the rfl tactic uses rfl to solve the goal, which sets ?rhs := lhs.
Other tactics, like congr, partially solve for ?rhs and create new goal metavariables
for each unsolved-for hole.
Once all the tactics have had a chance to run, conv mode itself uses rfl to solve
any remaining goals (note that in conv mode, every goal can be solved for by rfl!), and
then it uses the resulting lhs = rhs proof to rewrite the goal in the surrounding normal
tactic mode.
Conv tactics from Lean 4, Batteries, and Mathlib #
Unless they're annotated with "Batteries" or "Mathlib", the following tactics are defined in Lean 4 core.
Control #
donechecks that there are noconvgoals remaining.skipdoes nothing. It can be used to be the single tactic in an otherwise emptyconvblock. It does not skip aconvgoal.rflskips/closes aconvgoal by usingrfl. (Remember, the actual goal is⊢ lhs = ?rhs, so this sets?rhs := lhsand usesrflto provelhs = lhs.)conv => convSeqis a nestedconv. It usesconvto change the current goal without closing it. For example, this is how you can do aconv-targeted rewrite of the current expression and then applyconvtactics to the result.all_goals convSeqruns theconvtactics on everyconvgoal, collecting all the produced subgoals (if any).any_goals convSeqis likeall_goalsbut succeeds if the tactic sequence succeeds for any of the goals.case tag => convSeqfocuses on a goal with a given tag, runs the tactic sequence, and then auto-closes the focused goal withrfl. Has the same syntax as thecasetactic.case' tag => convSeqis likecasebut does not auto-close the goal if the tactics do not close it.next => convSeqandnext x1 ... xn => convSeqare like thenexttactic, but they auto-close the focused goal withrfl.· convSeqfocuses on the current goal and auto-closes it withrfl.focus => convSeqfocuses on the current goal. It does not auto-close the goal, unlikenext.{ convSeq }is likenext.first | convSeq1 | convSeq2 | ...tries eachconvsequence one at a time until one of them succeeds, or else fails.try convSeqruns theconvsequence and succeeds even if it fails. Same asfirst | convSeq | skip.repeat convSeqrepeatedly runsconvSequntil it fails.( convSeq )is for grouping. Useful when usingconvtactic combinators.conv1 <;> conv2is for runningconv1and runningconv2on every goal produced byconv.tactic => tacticSeqconverts the goal into⊢ lhs = ?rhsform and applies the tactic sequence. The tactic does not have to solve the goal completely, and remaining goals are turned back intoconvgoals. (Internal: there's also atactic' => tacticSeqthat does not remove theconvannotations from the goal before applying the tactic sequence.)discharge => tacticSeqtakes a goal| pwithpa proposition, uses the tactic sequence to prove⊢ p, and then closes the goal to convertptoTrue. (Mathlib)with_reducible convSeqchanges the transparency settings toreduciblewhile evaluating theconvsequence. (Mathlib)
Navigation #
congr(synonym:args) creates subgoals for every immediate subexpression of the expression. You can userflto skip any of these subgoals.lhs(synonym:left) traverses into the second-to-last argument of the expression. (Implemented usingcongr.)rhs(synonym:right) traverses into the last argument of the expression. (Implemented usingcongr.)arg i(andarg @i) traverses into theith explicit argument (resp. theith argument) of the expression. (Implemented usingcongr.)ext(synonym:intro) traverses into lambda, forall, andletexpressions.ext xgives the resulting binder the namex.ext x y z ...appliesextonce for each provided binder.enter [...]is a compact way to describe a path to a subterm.enter [i](whereiis a natural number) is equivalent toarg i.enter [@i]is equivalent toarg @i.enter [x](wherexis an identifier) is equivalent toext x.enter [a,b,c,...]isenter [a]; enter [b]; enter [c]; enter [...].
patternis for navigating into subexpressions that match a given patternpattern pattraverses to the first subterm of the target that matchespat.pattern (occs := *) pattraverses to every subterm of the target that matchespatwhich is not contained in another match ofpat. It generates one subgoal.pattern (occs := 1 2 4) patmatches occurrences1, 2, 4ofpatand produces three subgoals. Occurrences are numbered left to right from the outside in.
Manipulation #
change tchanges the expression totif the expression andtare definitionally equal.equals t => tacticSeqchanges the current expression, saye, tot, and asks you to prove the equalitye = t. (Batteries)rw [thms...]rewrites the expression using the given theorems. The syntax is similar torw.erw [thms...]rewrites the expression using the given theorems. The syntax is similar toerw.simp [thms...]appliessimpto rewrite the expression. The syntax is similar tosimp.dsimp [thms...]appliesdsimpto rewrite the expression. The syntax is similar todsimp.simp_matchsimplifiesmatchexpressions.apply eapplieseto the goal (which remember is⊢ lhs = ?rhs) using theapplytactic. Strange results may occur if the hypotheses ofeare not equalities.refine eapplieseto the goal (which remember is⊢ lhs = ?rhs) using therefinetactic. Strange results may occur if the placeholders ineare not equalities.exact ecloses the goal, wheree : lhs = ?rhs. (Batteries)Mathlib provides a number of tactics as
convtactics:abelandabel_nfringandring_nfnorm_castnorm_num1andnorm_numpush_neg
apply_congrapplies a relevant@[congr]lemma, which can be better suited for a function than the congruence lemma that thecongrtactic might generate. (Mathlib)slice i j(for category theory) reassociates a composition of morphisms to focus on the composition of morphismsithroughj. (Mathlib)
Reductions #
whnfreduces the expression to weak-head normal form.zetaapplies zeta reduction to the expression (i.e., substitutes allletexpressions and expands all local variables).reducereduces the expression like the#reducecommand. (Documentation says "for debugging purposes only.")unfold id1 id2 ...unfolds the definitions for the given constants using each definitions equational lemmas. For recursive definitions, only one layer of unfolding is performed.delta id1 id2 ...applies delta reduction for the given constants (i.e., substitutes the values of each constant). It is primitive: it ignores definitional equations and uses the raw definition of each constant. Usingunfoldis preferred.
Debugging, for internal use, or otherwise technical #
trace_stateprints the current goal state (runs thetrace_statetactic)fail_if_success convSeqfails if theconvsequence succeeds.guard_exprandguard_targetfor asserting that certain expressions are equal to others. (Batteries)unreachable!, which is the same as theunreachable!tactic. (Batteries)run_tac doSeqevaluates a monadic value and runs it as a tactic usingtactic'. (Mathlib)
Tactics and commands related to conv #
conv_lhs ... => ...andconv_rhs ... => ...are likeconv, but they immediately uselhsorrhs(respectively). (Mathlib)conv' ... => ...is likeconvbut assumes the goal is already annotated as aconvgoal. Used internally to go back and forth between tactic mode and conv mode.#conv convTactic => eis a command to apply theconvTacticto the expressione, yielding the converted expression (and dropping the generated proof). This is used to implement#simp,#whnf,#norm_num, and#push_neg. (Mathlib)