conv — Targeted Rewriting
conv lets you navigate into subexpressions and rewrite precisely where you want, solving the "I can't rewrite the right occurrence" problem.
The Problem conv Solves
lean
1-- Sometimes rw rewrites the wrong occurrence2example (a b : Nat) : (a + b) * (a + b) = (b + a) * (a + b) := by3 -- rw [Nat.add_comm] -- Would rewrite both occurrences!4 conv => 5 lhs -- focus on left-hand side6 arg 1 -- first argument of *7 rw [Nat.add_comm]8 -- Now goal is: (b + a) * (a + b) = (b + a) * (a + b)9 rflKey Takeaway
conv enters a "conversation mode" where you navigate to a subexpression and apply tactics to just that part.Navigation Commands
lean
1-- lhs / rhs: go to left or right side of equation2example (a b : Nat) : a + b = b + a := by3 conv => 4 lhs5 rw [Nat.add_comm]67-- arg n: go to nth argument (1-indexed)8example (a b c : Nat) : (a + b) + c = (b + a) + c := by9 conv =>10 lhs11 arg 1 -- go to (a + b)12 rw [Nat.add_comm]1314-- ext: introduce binders (for under lambdas/foralls)15example : (fun x : Nat => x + 0) = (fun x => x) := by16 conv =>17 lhs18 ext x -- introduce x19 rw [Nat.add_zero]Common conv Patterns
lean
1-- Rewrite inside a function argument2example (xs : List Nat) : xs.map (· + 0) = xs.map id := by3 conv =>4 lhs5 arg 1 -- the function argument6 ext x7 rw [Nat.add_zero]8 simp [id]910-- Rewrite one side of iff11example (P Q : Prop) (h : P ↔ Q) : (P ∧ True) ↔ (Q ∧ True) := by12 conv =>13 lhs14 arg 115 rw [h]1617-- Nested navigation18example (a b c d : Nat) : ((a + b) + c) + d = ((b + a) + c) + d := by19 conv =>20 lhs21 arg 1 -- (a + b) + c22 arg 1 -- a + b23 rw [Nat.add_comm]Tactics Inside conv
lean
1-- rfl: close with reflexivity2example (a : Nat) : a + 0 = a := by3 conv => lhs; rw [Nat.add_zero]45-- ring/omega inside conv6example (a b : Nat) : (a + b) * 2 = 2 * (b + a) := by7 conv => 8 rhs9 rw [Nat.add_comm]10 ring1112-- simp at specific location13example (xs : List Nat) : xs.reverse.reverse = xs := by14 conv => lhs; simpDeep Dive: conv Mode Navigation
Think of conv as zooming into your expression:
lean
1-- Expression: f (g (h x)) (k y)2-- 3-- conv =>4-- arg 1 -- f (g (h x)) (k y) → g (h x)5-- arg 1 -- g (h x) → h x6-- arg 1 -- h x → x78-- Other navigation:9-- pattern e -- go to subexpression matching pattern e10-- enter [n] -- same as arg n but for any position11-- all -- apply to all subexpressionspattern Matching
lean
1-- Navigate by pattern matching2example (a b c : Nat) : a + (b + c) = a + (c + b) := by3 conv =>4 rhs5 pattern b + c -- find and focus on b + c6 rw [Nat.add_comm]78-- Multiple patterns9example (a b : Nat) : (a + 0) + (b + 0) = a + b := by10 conv =>11 lhs12 pattern _ + 013 rw [Nat.add_zero]14 -- Still have one more to do15 conv =>16 lhs17 pattern _ + 018 rw [Nat.add_zero]ℹ
Use
pattern when the expression is complex and counting args would be error-prone.conv with simp
lean
1-- simp only at specific positions2example (xs : List Nat) (f : Nat → Nat) : 3 (xs.map f).length = xs.length := by4 conv => lhs; simp [List.length_map]56-- Avoid unwanted simplification7example (a b : Nat) : (a + b) * 1 = a + b := by8 conv => 9 lhs10 arg 2 -- the 111 -- don't simp the whole thing, just verify * 112 simp [Nat.mul_one]Changing Under Binders
lean
1-- ext to go under lambda2example : (fun x : Nat => x * 2) = (fun x => 2 * x) := by3 conv =>4 lhs5 ext x6 rw [Nat.mul_comm]78-- Under forall9example : (∀ x : Nat, x + 0 = x) ↔ (∀ x : Nat, x = x) := by10 constructor11 · intro h x12 have := h x13 simp at this14 exact this15 · intro _ x16 simpExamples
Specific Occurrence Rewrite
lean
1example (a b c : Nat) : a + b + c = c + b + a := by2 conv =>3 lhs4 rw [Nat.add_assoc]5 arg 26 rw [Nat.add_comm]7 conv =>8 lhs9 rw [← Nat.add_assoc]10 arg 111 rw [Nat.add_comm]12 ringSimplify Function Body
lean
1example (f : Nat → Nat) (h : ∀ n, f n = n + 1) : 2 (fun x => f x + 1) = (fun x => x + 2) := by3 conv =>4 lhs5 ext x6 rw [h]7 ring_nfRewrite in Hypothesis
lean
1example (a b : Nat) (h : a + b = 10) : b + a = 10 := by2 conv at h =>3 lhs4 rw [Nat.add_comm]5 exact h