Zulip Chat Archive
Stream: lean4
Topic: Missing inductive hypothesis in constr w/ different type
Andrés Goens (Aug 13 2024 at 12:23):
I'm missing an inductive hypothesis when trying to use induction on a constructor that has a different type for the recusion. Here's an MWE:
inductive Foo
| base : Foo
| reccase : Foo → Foo
| natcase : Nat → Foo
def decFoo : Foo → Foo
| .base => .base
| .reccase f => f
| .natcase .zero => .base
| .natcase (.succ n) => .natcase n
theorem decFoo_decrease (f : Foo) : sizeOf (decFoo f) ≤ sizeOf f :=
by induction f --<;> simp [decFoo]
/-
3 goals
case base
⊢ sizeOf (decFoo Foo.base) ≤ sizeOf Foo.base
case reccase
a✝ : Foo
a_ih✝ : sizeOf (decFoo a✝) ≤ sizeOf a✝
⊢ sizeOf (decFoo a✝.reccase) ≤ sizeOf a✝.reccase
case natcase
a✝ : Nat
⊢ sizeOf (decFoo (Foo.natcase a✝)) ≤ sizeOf (Foo.natcase a✝)
-/
On the regular recursive case .reccase
we get the induction hypothesis a_ih✝ : sizeOf (decFoo a✝) ≤ sizeOf a✝
, but on the other recursive case that uses another inductive type (Nat
in this case), it's missing. If I look at the recursor, it's there though:
#check Foo.rec
/-
Foo.rec.{u} {motive : Foo → Sort u}
(base : motive Foo.base)
(reccase : (a : Foo) → motive a → motive a.reccase)
(natcase : (a : Nat) → motive (Foo.natcase a))
(t : Foo) : motive t
-/
Is that a bug/missing feature in the induction
tactic? (Maybe @Joachim Breitner is the person to ask here?)
Henrik Böving (Aug 13 2024 at 12:27):
The recursor is not giving you an IH here?
Henrik Böving (Aug 13 2024 at 12:27):
There is also no induction to be done in that case, natcase
only contains a Nat
, your induction goes over a Foo
.
Andrés Goens (Aug 13 2024 at 12:29):
hm, you're right @Henrik Böving maybe this was too minimal, I have to think a bit more about why it's not working in my actual case
Henrik Böving (Aug 13 2024 at 12:29):
theorem decFoo_decrease (f : Foo) : sizeOf (decFoo f) ≤ sizeOf f := by
induction f with
| base => simp [decFoo]
| reccase f ih => simp [decFoo, ih]
| natcase n =>
cases n with
| zero => simp [decFoo]
| succ n => simp [decFoo]
Andrés Goens (Aug 13 2024 at 12:51):
yep, that was quite silly! My actual case was just a more complicated version of the same. I needed to do an induction in the other type, but the reasoning from @Henrik Böving still applied. Thanks!
Henrik Böving (Aug 13 2024 at 12:52):
Note that you can also use functional induction here (don't know if that applies to your real world problem)
Andrés Goens (Aug 13 2024 at 14:33):
(functional induction works better in my actual use case too, thanks!)
Andrés Goens (Aug 13 2024 at 14:40):
wanted to write it up for the MWE for completeness here, but interestingly I can't get it to work :sweat_smile:
inductive Foo
| base : Foo
| reccase : Foo → Foo
| natcase : Nat → Foo
def decFoo : Foo → Foo
| .base => .base
| .reccase f => f
| .natcase .zero => .base
| .natcase (.succ n) => .natcase n
#check decFoo -- decFoo : Foo → Foo
#check decFoo.induct -- unknown constant 'decFoo.induct'
Joachim Breitner (Aug 13 2024 at 14:53):
You only get functional induction for recursive functions (at the moment, at least), so it’s not applicable to your proof. But it is also an induction
that you don't need induction, merely case
Joachim Breitner (Aug 13 2024 at 14:54):
Or even just
theorem decFoo_decrease (f : Foo) : sizeOf (decFoo f) ≤ sizeOf f := by
unfold decFoo; split <;> simp
Andrés Goens (Aug 13 2024 at 14:56):
makes sense, thanks! (FWIW, in the non-minimal case it is recursive and functional induction worked like a charm)
Joachim Breitner (Aug 13 2024 at 14:57):
Ah, the danger of mwe-ification. Glad to hear it works for you!
Last updated: May 02 2025 at 03:31 UTC