Zulip Chat Archive
Stream: metaprogramming / tactics
Topic: Grind Tactic : split/cases for non-inductive type
Dolores Haze (Dec 31 2025 at 21:29):
Hello,
As I understand, the grind tactic can do case-splitting on inductive types like this
@[grind cases]
inductive IsEven : Nat → Prop
| zero : IsEven 0
| step : IsEven n → IsEven (n + 2)
/--
[grind.split] IsEven 5, generation: 0
[grind.split] IsEven n, generation: 1
-/
example (h : IsEven (n := n) 5) : False := by
grind
Now, If I have a type that is not an inductive type, how can I guide the grind tactic to do case splitting? Say I have this:
def DefEven {n : Nat} : Nat → Prop := IsEven (n := n)
-- this fails as grind can't do case splitting for DefEven
example (h : DefEven (n := n) 5) : False := by
grind
I understand that I can : 1) make the DefEven definition reducible, or 2) unfold it before running grind in my example. So, I'm wondering if there's another way to provide my own case splitting for grind?
The reason is, in my real use case, my type is defined to be a List, and I'm transferring many nice facts about List while I can still some modifications that suits my own application. However, I lose some nice properties so I was hoping I could find a hack were I provide nil/cons theorems for my custom type and still could benefit from grind.
Aaron Liu (Dec 31 2025 at 21:38):
you can make it an abbrev
Dolores Haze (Dec 31 2025 at 21:48):
Aaron Liu said:
you can make it an
abbrev
Thanks. Yes I know about abbrev; that's why I mentioned in my original post that I can make the definition reduicible.
But My question is is there a way to not abbrev my type and instead manually guide the grind? (e.g., by providing theorems like DefEven.zero and DefEven.step ?
Aaron Liu (Dec 31 2025 at 21:54):
you can unfold DefEven at *
Robin Arnez (Dec 31 2025 at 21:55):
Well you can provide a lemma to the effect of case splitting like:
@[grind →]
theorem DefEven.unstep : DefEven (n + 2) → DefEven n
| .step h => h
@[grind .]
theorem DefEven.no_one : ¬DefEven 1 := nofun
Robin Arnez (Dec 31 2025 at 21:56):
Now, my guess is that, in your real application, you should use a one-field structure instead:
@[grind cases]
structure StructEven (n : Nat) where
ofIsEven ::
toIsEven : IsEven n
Robin Arnez (Dec 31 2025 at 21:57):
or well, something like
@[grind cases]
structure MyCustomList where
ofList :: toList : List Nat
Dolores Haze (Dec 31 2025 at 22:33):
Robin Arnez said:
Well you can provide a lemma to the effect of case splitting like:
Thank you. This is very close to what I was looking for.
or well, something like
I wasn't aware of the one-field structure. I have to learn more about this.
Ka Wing Li (Jan 01 2026 at 21:36):
I have a similar question: how to let grind unfold the le/lt instance?
structure Word where
ofBitVec :: toBitVec : BitVec 64
namespace Word
instance : LE Word where
le x y := x.toBitVec.toNat ≤ y.toBitVec.toFin
instance : LT Word where
lt x y := x.toBitVec.toNat < y.toBitVec.toFin
instance : Preorder Word where
le_refl x := by simp [instLE]
le_trans x y z := by simp [instLE]; grind
lt_iff_le_not_ge := by simp [instLE, instLT]; grind
end Word
Ka Wing Li (Jan 01 2026 at 21:36):
I seems have to unfold the instLE/instLT everywhere before applying grind.
Aaron Liu (Jan 01 2026 at 21:45):
write a le_def theorem and have grind use that
Ka Wing Li (Jan 01 2026 at 21:53):
You mean something like this
@[grind =]
theorem le_def {x y : Addr} : x ≤ y ↔ x.toBitVec.toNat ≤ y.toBitVec.toNat := by rfl
Ka Wing Li (Jan 01 2026 at 21:53):
Thanks! It works like a charm.
Last updated: Feb 28 2026 at 14:05 UTC