This PR implements the option `revert`, which is set to `false` by default. To recover the old `grind` behavior, you should use `grind +revert`. Previously, `grind` used the `RevSimpIntro` idiom, i.e., it would revert all hypotheses and then re-introduce them while simplifying and applying eager `cases`. This idiom created several problems: * Users reported that `grind` would include unnecessary parameters. See [here](https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/Grind.20aggressively.20includes.20local.20hypotheses.2E/near/554887715). * Unnecessary section variables were also being introduced. See the new test contributed by Sebastian Graf. * Finally, it prevented us from supporting arbitrary parameters as we do in `simp`. In `simp`, I implemented a mechanism that simulates local universe-polymorphic theorems, but this approach could not be used in `grind` because there is no mechanism for reverting (and re-introducing) local universe-polymorphic theorems. Adding such a mechanism would require substantial work: I would need to modify the local context object. I considered maintaining a substitution from the original variables to the new ones, but this is also tricky, because the mapping would have to be stored in the `grind` goal objects, and it is not just a simple mapping. After reverting everything, I would need to keep a sequence of original variables that must be added to the mapping as we re-introduce them, but eager case splits complicate this quite a bit. The whole approach felt overly messy. The new behavior `grind -revert` addresses all these issues. None of the `grind` proofs in our test suite broke after we fixed the bugs exposed by the new feature. That said, the traces and counterexamples produced by `grind` are different. The new proof terms are also different.
66 lines
2.2 KiB
Text
66 lines
2.2 KiB
Text
/-
|
||
This is an example posted by Wrenna Robson on Zulip.
|
||
-/
|
||
|
||
-- Reference
|
||
def idxToCount [BEq α] (xs : List α) (i : Nat) (hi : i < xs.length) : Nat := go xs i hi 0 where
|
||
@[specialize] go : (xs : List α) → (i : Nat) → i < xs.length → Nat → Nat
|
||
| _ :: _, 0, _, acc => acc
|
||
| x :: xs, i + 1, hi, acc =>
|
||
haveI hi := Nat.lt_of_succ_lt_succ hi
|
||
bif x == xs[i] then go xs i hi (acc + 1) else go xs i hi acc
|
||
|
||
-- `go` does not depend on the outer `xs` as expected.
|
||
/--
|
||
info: idxToCount.go.{u_1} {α : Type u_1} [BEq α] (xs : List α) (i : Nat) : i < xs.length → Nat → Nat
|
||
-/
|
||
#guard_msgs in
|
||
#check idxToCount.go
|
||
|
||
|
||
def idxToCount' [BEq α] (xs : List α) (i : Nat) (hi : i < xs.length) : Nat := go xs i hi 0 where
|
||
@[specialize] go : (xs : List α) → (i : Nat) → i < xs.length → Nat → Nat
|
||
| _ :: _, 0, _, acc => acc
|
||
| x :: xs, i + 1, hi, acc =>
|
||
bif x == xs[i]'(by grind) then go xs i (by grind) (acc + 1) else go xs i (by grind) acc
|
||
|
||
/-
|
||
`grind +revert` was the default behavior until v4.25.1
|
||
Thus, `go` used to depend on the outer `xs`. This is not the case anymore.
|
||
-/
|
||
/--
|
||
info: idxToCount'.go.{u_1} {α : Type u_1} [BEq α] (xs : List α) (i : Nat) : i < xs.length → Nat → Nat
|
||
-/
|
||
#guard_msgs in
|
||
#check idxToCount'.go
|
||
|
||
/-
|
||
We can reproduce the behavior of v4.25.1 using `grind +revert`.
|
||
-/
|
||
def idxToCount'' [BEq α] (xs : List α) (i : Nat) (hi : i < xs.length) : Nat := go xs i hi 0 where
|
||
@[specialize] go : (xs : List α) → (i : Nat) → i < xs.length → Nat → Nat
|
||
| _ :: _, 0, _, acc => acc
|
||
| x :: xs, i + 1, hi, acc =>
|
||
bif x == xs[i]'(by grind +revert) then go xs i (by grind +revert) (acc + 1) else go xs i (by grind +revert) acc
|
||
|
||
/--
|
||
info: idxToCount''.go.{u_1} {α : Type u_1} [BEq α] (xs : List α) (i : Nat) (hi : i < xs.length) (xs✝ : List α) (i✝ : Nat) :
|
||
i✝ < xs✝.length → Nat → Nat
|
||
-/
|
||
#guard_msgs in
|
||
#check idxToCount''.go
|
||
|
||
set_option linter.unusedVariables true
|
||
-- Another example from the Zulip thread
|
||
/--
|
||
warning: unused variable `hi`
|
||
|
||
Note: This linter can be disabled with `set_option linter.unusedVariables false`
|
||
-/
|
||
#guard_msgs in
|
||
def oops (oh_no : List Nat) (hi : 0 < oh_no.length) : True := go where
|
||
go : True := by grind
|
||
|
||
/-- info: oops.go : True -/
|
||
#guard_msgs in
|
||
#check oops.go
|