10.6. conv によるターゲットの書き換え(Targeted Rewriting with conv🔗

conv 、もしくは変換タクティクはゴール内のターゲットを絞った書き換えを可能にします。 conv の引数はメインのタクティク言語と相互に連携する別の言語で記述されます;これはゴール内の特定の部分項に移動するコマンドと、これらの部分項を書き換えるコマンドを備えています。 conv は書き換えをゴール全体ではなく、ゴールの一部(例えば等式の片側のみ)にのみ適用する場合や、 rw のようなタクティクが項にアクセスするのを防いでしまう束縛子の中の書き換えを適用する場合に便利です。

変換タクティク言語はメインのタクティク言語に非常に似ています:同じ証明状態を使用し、タクティクは主にメインゴールに対して作用し、新しいゴールの列で失敗または成功する可能性があり、マクロ展開はタクティクの実行と交互に行われます。タクティクが最終的にゴールを解決するためのものであるメインのタクティク言語とは異なり、 conv はゴールを 変更 するために用いられます。これによってゴールはその後のメインのタクティク言語で容易に処理できるようになります。 conv で書き換えることを意図しているゴールはターンスタイルの代わりに縦棒で表示されます。

🔗tactic
conv

conv => ... allows the user to perform targeted rewriting on a goal or hypothesis, by focusing on particular subexpressions.

See https://lean-lang.org/theorem_proving_in_lean4/conv.html for more details.

Basic forms:

  • conv => cs will rewrite the goal with conv tactics cs.

  • conv at h => cs will rewrite hypothesis h.

  • conv in pat => cs will rewrite the first subexpression matching pat (see pattern).

conv による移動と書き換え

この例では、加算のインスタンスが複数存在し、 rw はデフォルトで最初に出会ったインスタンスを書き換えてしまいます。書き換える前に特定の部分項へ移動するために conv を使用すると、 rw は正しい項を書き換える以外に選択肢がなくなります。

example (x y z : Nat) : x + (y + z) = (x + z) + y := x:Naty:Natz:Natx + (y + z) = x + z + y conv => x:Naty:Natz:Nat| x + (y + z) x:Naty:Natz:Nat| y + z rw [x:Naty:Natz:Nat| z + y] All goals completed! 🐙
conv による束縛子の中の書き換え

この例では、加算は束縛子の中に存在するため、 rw は使えません。しかし、 conv を使用して関数本体に移動すると成功します。入れ子になった conv の使用は、その部分項の1つに対してさらなる変換を実行したのち、制御を項の中の現在の位置に戻します。ゴールは書き換え後の反射性の等式であるため、 conv は自動的にこれを閉じます。

example : (fun (x y z : Nat) => x + (y + z)) = (fun x y z => (z + x) + y) := (fun x y z => x + (y + z)) = fun x y z => z + x + y conv => | fun x y z => x + (y + z) x:Naty:Natz:Nat| x + (y + z) conv => x:Naty:Natz:Nat| y + z rw [x:Naty:Natz:Nat| z + y] rw [x:Naty:Natz:Nat| x + z + y] x:Naty:Natz:Nat| x + z rw [x:Naty:Natz:Nat| z + x]

10.6.1. 制御構造(Control Structures)🔗

🔗conv tactic
first

first | conv | ... runs each conv until one succeeds, or else fails.

🔗conv tactic
try

try tac runs tac and succeeds even if tac failed.

🔗conv tactic
<;>
🔗conv tactic
repeat

repeat convs runs the sequence convs repeatedly until it fails to apply.

🔗conv tactic
skip

skip does nothing.

🔗conv tactic
{ ... }

{ convs } runs the list of convs on the current target, and any subgoals that remain are trivially closed by skip.

🔗conv tactic
( ... )

(convs) runs the convs in sequence on the current list of targets. This is pure grouping with no added effects.

🔗conv tactic
done

done succeeds iff there are no goals remaining.

10.6.2. ゴールの選択(Goal Selection)🔗

🔗conv tactic
all_goals

all_goals tac runs tac on each goal, concatenating the resulting goals, if any.

🔗conv tactic
any_goals

any_goals tac applies the tactic tac to every goal, and succeeds if at least one application succeeds.

🔗conv tactic
case ... => ...
  • case tag => tac focuses on the goal with case name tag and solves it using tac, or else fails.

  • case tag x₁ ... xₙ => tac additionally renames the n most recent hypotheses with inaccessible names to the given names.

  • case tag₁ | tag₂ => tac is equivalent to (case tag₁ => tac); (case tag₂ => tac).

🔗conv tactic
case' ... => ...

case' is similar to the case tag => tac tactic, but does not ensure the goal has been solved after applying tac, nor admits the goal if tac failed. Recall that case closes the goal using sorry when tac fails, and the tactic execution is not interrupted.

🔗conv tactic
next ... => ...

next => tac focuses on the next goal and solves it using tac, or else fails. next x₁ ... xₙ => tac additionally renames the n most recent hypotheses with inaccessible names to the given names.

🔗conv tactic
focus

focus tac focuses on the main goal, suppressing all other goals, and runs tac on it. Usually · tac, which enforces that the goal is closed by tac, should be preferred.

🔗conv tactic
. ...

· conv focuses on the main conv goal and tries to solve it using s.

🔗conv tactic
· ...

· conv focuses on the main conv goal and tries to solve it using s.

🔗conv tactic
fail_if_success

fail_if_success t fails if the tactic t succeeds.

10.6.3. 移動(Navigation)🔗

🔗conv tactic
lhs

Traverses into the left subterm of a binary operator.

In general, for an n-ary operator, it traverses into the second to last argument. It is a synonym for arg -2.

🔗conv tactic
rhs

Traverses into the right subterm of a binary operator.

In general, for an n-ary operator, it traverses into the last argument. It is a synonym for arg -1.

🔗conv tactic
fun

Traverses into the function of a (unary) function application. For example, | f a b turns into | f a. (Use arg 0 to traverse into f.)

🔗conv tactic
congr

Performs one step of "congruence", which takes a term and produces subgoals for all the function arguments. For example, if the target is f x y then congr produces two subgoals, one for x and one for y.

🔗conv tactic
arg [@]i
  • arg i traverses into the i'th argument of the target. For example if the target is f a b c d then arg 1 traverses to a and arg 3 traverses to c. The index may be negative; arg -1 traverses into the last argument, arg -2 into the second-to-last argument, and so on.

  • arg @i is the same as arg i but it counts all arguments instead of just the explicit arguments.

  • arg 0 traverses into the function. If the target is f a b c d, arg 0 traverses into f.

syntax
enterArg ::= ...
    | num
enterArg ::= ...
    | @num
enterArg ::= ...
    | ident
🔗conv tactic
enter

enter [arg, ...] is a compact way to describe a path to a subterm. It is a shorthand for other conv tactics as follows:

  • enter [i] is equivalent to arg i.

  • enter [@i] is equivalent to arg @i.

  • enter [x] (where x is an identifier) is equivalent to ext x. For example, given the target f (g a (fun x => x b)), enter [1, 2, x, 1] will traverse to the subterm b.

🔗conv tactic
pattern
  • pattern pat traverses to the first subterm of the target that matches pat.

  • pattern (occs := *) pat traverses to every subterm of the target that matches pat which is not contained in another match of pat. It generates one subgoal for each matching subterm.

  • pattern (occs := 1 2 4) pat matches occurrences 1, 2, 4 of pat and produces three subgoals. Occurrences are numbered left to right from the outside in.

Note that skipping an occurrence of pat will traverse inside that subexpression, which means it may find more matches and this can affect the numbering of subsequent pattern matches. For example, if we are searching for f _ in f (f a) = f b:

  • occs := 1 2 (and occs := *) returns | f (f a) and | f b

  • occs := 2 returns | f a

  • occs := 2 3 returns | f a and | f b

  • occs := 1 3 is an error, because after skipping f b there is no third match.

🔗conv tactic
ext

ext x traverses into a binder (a fun x => e or x, e expression) to target e, introducing name x in the process.

🔗conv tactic
args

args traverses into all arguments. Synonym for congr.

🔗conv tactic
left

left traverses into the left argument. Synonym for lhs.

🔗conv tactic
right

right traverses into the right argument. Synonym for rhs.

🔗conv tactic
intro

intro traverses into binders. Synonym for ext.

10.6.4. ゴールの変更(Changing the Goal)🔗

10.6.4.1. 簡約(Reduction)🔗

🔗conv tactic
whnf

Reduces the target to Weak Head Normal Form. This reduces definitions in "head position" until a constructor is exposed. For example, List.map f [a, b, c] weak head normalizes to f a :: List.map f [b, c].

🔗conv tactic
reduce

Puts term in normal form, this tactic is meant for debugging purposes only.

🔗conv tactic
zeta

Expands let-declarations and let-variables.

🔗conv tactic
delta

delta id1 id2 ... unfolds all occurrences of id1, id2, ... in the target. Like the delta tactic, this ignores any definitional equations and uses primitive delta-reduction instead, which may result in leaking implementation details. Users should prefer unfold for unfolding definitions.

🔗conv tactic
unfold
  • unfold id unfolds all occurrences of definition id in the target.

  • unfold id1 id2 ... is equivalent to unfold id1; unfold id2; ....

Definitions can be either global or local definitions.

For non-recursive global definitions, this tactic is identical to delta. For recursive global definitions, it uses the "unfolding lemma" id.eq_def, which is generated for each recursive definition, to unfold according to the recursive definition given by the user. Only one level of unfolding is performed, in contrast to simp only [id], which unfolds definition id recursively.

This is the conv version of the unfold tactic.

10.6.4.2. 単純化(Simplification)🔗

🔗conv tactic
simp

simp [thm] performs simplification using thm and marked @[simp] lemmas. See the simp tactic for more information.

🔗conv tactic
dsimp

dsimp is the definitional simplifier in conv-mode. It differs from simp in that it only applies theorems that hold by reflexivity.

Examples:

example (a : Nat): (0 + 0) = a - a := a:Nat0 + 0 = a - a conv => a:Nat| 0 + 0 a:Nat| 0 rw [a:Nat| a - a]
🔗conv tactic
simp_match

simp_match simplifies match expressions. For example,

match [a, b] with | [] => 0 | hd :: tl => hd

simplifies to a.

10.6.4.3. 書き換え(Rewriting)🔗

🔗conv tactic
change

change t' replaces the target t with t', assuming t and t' are definitionally equal.

🔗conv tactic
rewrite

rw [thm] rewrites the target using thm. See the rw tactic for more information.

🔗conv tactic
rw

rw [rules] applies the given list of rewrite rules to the target. See the rw tactic for more information.

🔗conv tactic
erw

erw [rules] is a shorthand for rw (transparency := .default) [rules]. This does rewriting up to unfolding of regular definitions (by comparison to regular rw which only unfolds @[reducible] definitions).

🔗conv tactic
apply

The apply thm conv tactic is the same as apply thm the tactic. There are no restrictions on thm, but strange results may occur if thm cannot be reasonably interpreted as proving one equality from a list of others.

10.6.5. 入れ子のタクティク(Nested Tactics)🔗

🔗tactic
conv'

Executes the given conv block without converting regular goal into a conv goal.

🔗conv tactic
tactic

Focuses, converts the conv goal lhs into a regular goal lhs = rhs, and then executes the given tactic block.

🔗conv tactic
tactic'

Executes the given tactic block without converting conv goal into a regular goal.

🔗tactic
conv'

Executes the given conv block without converting regular goal into a conv goal.

🔗conv tactic
conv => ...

conv => cs runs cs in sequence on the target t, resulting in t', which becomes the new target subgoal.

10.6.6. デバッグ用ユーティリティ(Debugging Utilities)🔗

🔗conv tactic
trace_state

trace_state prints the current goal state.

10.6.7. その他(Other)🔗

🔗conv tactic
rfl

rfl closes one conv goal "trivially", by using reflexivity (that is, no rewriting).

🔗conv tactic
norm_cast

norm_cast tactic in conv mode.