The convert tactic. #
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.
Close the goal g using Eq.mp v e,
where v is a metavariable asserting that the type of g and e are equal.
Then call MVarId.congrN! (also using local hypotheses and reflexivity) on v,
and return the resulting goals.
With symm = true, reverses the equality in v, and uses Eq.mpr v e instead.
With depth = some n, calls MVarId.congrN! n instead, with n as the max recursion depth.
Equations
Instances For
Replaces the type of the local declaration fvarId with typeNew,
using Lean.MVarId.congrN! to prove that the old type of fvarId is equal to typeNew.
Uses Lean.MVarId.replaceLocalDecl to replace the type.
Returns the new goal along with the side goals generated by congrN!.
With symm = true, reverses the equality,
changing the goal to prove typeNew is equal to typeOld.
With depth = some n, calls MVarId.congrN! n instead, with n as the max recursion depth.
Equations
Instances For
convert e, where the term e is inferred to have type t, replaces the main goal ⊢ t' with new
goals for proving the equality t' = t using congruence. The goals are created like congr! would.
Like refine e, any holes (?_ or ?x) in e that are not solved by unification are converted
into new goals, using the hole's name, if any, as the goal case name.
Like congr!, convert introduces variables while applying congruence rules. These can be
pattern-matched, like rintro would, using the with keyword.
See also convert_to t, where t specifies the expected type, instead of a proof term of type t.
In other words, convert_to t works like convert (?_ : t). Both tactics use the same options.
convert ← ecreates equality goals in the opposite direction (with the goal type on the right).convert e using n, wherenis a positive numeral, controls the depth with which congruence is applied. For example, if the main goal is⊢ Prime (n + n + 1)ande : Prime (2 * n + 1), thenconvert e using 2results in one goal,⊢ n + n = 2 * n, andconvert e using 3(or more) results in two (impossible) goals⊢ HAdd.hAdd = HMul.hMuland⊢ n = 2. By default, the depth is unlimited.convert e with x ⟨y₁, y₂⟩ (z₁ | z₂)names or pattern-matches the variables introduced by congruence rules, likerintro x ⟨y₁, y₂⟩ (z₁ | z₂)would.convert (config := cfg) euses the configuration options incfgto control the congruence rules (seeCongr!.Config).
Examples:
-- `convert using` controls the depth of congruence.
example {n : ℕ} (e : Prime (2 * n + 1)) :
Prime (n + n + 1) := by
convert e using 2
-- One goal: ⊢ n + n = 2 * n
ring
-- `convert` can fail where `exact` succeeds.
example (h : p 0) : p 1 := by
fail_if_success
convert h -- fails, left-over goal 1 = 0
done
exact h -- succeeds
```lean
-- `convert with` names introduced variables.
example (p q : Nat → Prop) (h : ∀ ε > 0, p ε) :
∀ ε > 0, q ε := by
convert h using 2 with ε hε
-- Goal now looks like:
-- hε : ε > 0
-- ⊢ q ε ↔ p ε
sorry
Equations
Instances For
Elaborates term ensuring the expected type, allowing stuck metavariables.
Returns stuck metavariables as additional goals.
Equations
Instances For
convert_to t on a goal ⊢ t' changes the goal to ⊢ t and adds new goals for proving the
equality t' = t using congruence. The goals are created like congr! would.
Any remaining congruence goals come before the main goal.
Like refine e, any holes (?_ or ?x) in t that are not solved by unification are converted
into new goals, using the hole's name, if any, as the goal case name.
Like congr!, convert_to introduces variables while applying congruence rules. These can be
pattern-matched, like rintro would, using the with keyword.
convert e, where e is a term of type t, uses e to close the new main goal. In other words,
convert e works like convert_to t; refine e. Both tactics use the same options.
convert_to ty at hchanges the type of the local hypothesishtoty. If later local hypotheses or the goal depend onh, thenconvert_to t at hmay leave a copy ofh.convert_to ← tcreates equality goals in the opposite direction (with the original goal type on the right).convert_to t using n, wherenis a positive numeral, controls the depth with which congruence is applied. For example, if the main goal is⊢ Prime (n + n + 1), thenconvert_to Prime (2 * n + 1) using 2results in one goal,⊢ n + n = 2 * n, andconvert_to Prime (2 * n + 1) using 3(or more) results in two (impossible) goals⊢ HAdd.hAdd = HMul.hMuland⊢ n = 2. The default value fornis 1.convert_to t with x ⟨y₁, y₂⟩ (z₁ | z₂)names or pattern-matches the variables introduced by congruence rules, likerintro x ⟨y₁, y₂⟩ (z₁ | z₂)would.convert_to (config := cfg) tuses the configuration options incfgto control the congruence rules (seeCongr!.Config).
Equations
Instances For
ac_change t on a goal ⊢ t' changes the goal to ⊢ t and adds new goals for proving the equality
t' = t using congruence, then tries proving these goals by associativity and commutativity. The
goals are created like congr! would.
In other words, ac_change t is like convert_to t followed by ac_refl.
Like refine e, any holes (?_ or ?x) in t that are not solved by unification are converted
into new goals, using the hole's name, if any, as the goal case name.
Like congr!, convert_to introduces variables while applying congruence rules. These can be
pattern-matched, like rintro would, using the with keyword.
ac_change t using n, wherenis a positive numeral, controls the depth with which congruence is applied. For example, if the main goal is⊢ Prime ((a * b + 1) + c), thenac_change Prime ((1 + a * b) + c) using 2solves the side goals, andac_change Prime ((1 + a * b) + c) using 3(or more) results in two (impossible) goals⊢ 1 = a * band⊢ a * b = 1. The default value fornis 1.
Example:
example (a b c d e f g N : ℕ) : (a + b) + (c + d) + (e + f) + g ≤ N := by
ac_change a + d + e + f + c + g + b ≤ _
-- ⊢ a + d + e + f + c + g + b ≤ N