This PR adds a linter (`linter.unusedSimpArgs`) that complains when a simp argument (`simp [foo]`) is unused. It should do the right thing if the `simp` invocation is run multiple times, e.g. inside `all_goals`. It does not trigger when the `simp` call is inside a macro. The linter message contains a clickable hint to remove the simp argument. I chose to display a separate warning for each unused argument. This means that the user has to click multiple times to remove all of them (and wait for re-elaboration in between). But this just means multiple endorphine kicks, and the main benefit over a single warning that would have to span the whole argument list is that already the squigglies tell the users about unused arguments. This closes #4483. Making Init and Std clean wrt to this linter revealed close to 1000 unused simp args, a pleasant experience for anyone enjoying tidying things: #8905
66 lines
2 KiB
Text
66 lines
2 KiB
Text
/-!
|
||
This test checks that simp is able to instantiate the parameter `g` below. It does not
|
||
appear on the lhs of the rule, but solving `hf` fully determines it.
|
||
-/
|
||
|
||
example (l : List Nat) :
|
||
l.attach.foldl (fun acc t => max acc (t.val)) 0 = l.foldl (fun acc t => max acc t) 0 := by
|
||
simp [List.foldl_subtype]
|
||
|
||
example (l : List Nat) :
|
||
l.attach.foldl (fun acc ⟨t, _⟩ => max acc t) 0 = l.foldl (fun acc t => max acc t) 0 := by
|
||
simp [List.foldl_subtype]
|
||
|
||
|
||
theorem foldr_to_sum (l : List Nat) (f : Nat → Nat → Nat) (g : Nat → Nat)
|
||
(h : ∀ acc x, f x acc = g x + acc) :
|
||
l.foldr f 0 = List.sum (l.map g) := by
|
||
rw [List.sum, List.foldr_map]
|
||
congr
|
||
funext x acc
|
||
apply h
|
||
|
||
-- this works:
|
||
example (l : List Nat) :
|
||
l.foldr (fun x a => x*x + a) 0 = List.sum (l.map (fun x => x * x)) := by
|
||
simp [foldr_to_sum]
|
||
|
||
-- this does not, so such a pattern is still quite fragile
|
||
|
||
/--
|
||
error: unsolved goals
|
||
l : List Nat
|
||
⊢ List.foldr (fun x a => a + x * x) 0 l = (List.map (fun x => x * x) l).sum
|
||
-/
|
||
#guard_msgs in
|
||
set_option linter.unusedSimpArgs false in
|
||
example (l : List Nat) :
|
||
l.foldr (fun x a => a + x*x) 0 = List.sum (l.map (fun x => x * x)) := by
|
||
simp [foldr_to_sum]
|
||
|
||
-- but with stronger simp normal forms, it would work:
|
||
|
||
@[simp]
|
||
theorem add_eq_add_cancel (a x y : Nat) : (a + x = y + a) ↔ (x = y) := by omega
|
||
|
||
example (l : List Nat) :
|
||
l.foldr (fun x a => a + x*x) 0 = List.sum (l.map (fun x => x * x)) := by
|
||
simp [foldr_to_sum]
|
||
|
||
|
||
-- An example with zipWith
|
||
|
||
theorem zipWith_ignores_right
|
||
(l₁ : List α) (l₂ : List β) (f : α → β → γ) (g : α → γ)
|
||
(h : ∀ a b, f a b = g a) :
|
||
List.zipWith f l₁ l₂ = List.map g (l₁.take l₂.length) := by
|
||
induction l₁ generalizing l₂ with
|
||
| nil => simp
|
||
| cons x xs ih =>
|
||
cases l₂
|
||
· simp
|
||
· simp [List.zipWith, h, ih]
|
||
|
||
example (l₁ l₂ : List Nat) (hlen: l₁.length = l₂.length):
|
||
List.zipWith (fun x _ => x*x) l₁ l₂ = l₁.map (fun x => x * x) := by
|
||
simp only [zipWith_ignores_right, hlen.symm, List.take_length]
|