Zulip Chat Archive

Stream: mathlib4

Topic: default induction


Yury G. Kudryashov (Feb 15 2023 at 04:36):

Can we teach Lean 4 to do induction on Nat (in induction tactic) using 0 and n + 1 instead of Nat.zero and Nat.succ?

Kevin Buzzard (Feb 15 2023 at 09:45):

Isn't a custom induction lemma the way to do this (and then induction using lemma)?

Eric Wieser (Feb 15 2023 at 09:59):

I think there is a way to set a default induction principle now too?

Kevin Buzzard (Feb 15 2023 at 11:47):

That would be really nice. In NNG I had to hack Lean's induction tactic to make it use the right zero (mynat.zero v has_zero.zero)

Reid Barton (May 29 2023 at 22:56):

Is there any update on this? It would be very nice for WithTop as well.
As a bonus: The "Generate a recursive function..." code action should also generate the correct pattern match syntax for the type...

Mario Carneiro (May 29 2023 at 22:58):

I was thinking about using the registered delaborators in the code action, which will fix pattern matches on [] / x :: xs, but not 0 / n + 1

Eric Wieser (May 29 2023 at 23:02):

Unless I was imagining things, I think it turns out that in Lean 3 we had this all along by just naming the recursor rec; though that only worked for type aliases.

Kyle Miller (May 29 2023 at 23:03):

@Reid Barton There's an @[eliminator] attribute: https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/constructor.20is.20the.20new.20split/near/346658539

I tried experimenting with it again recently and thought I ran into some error with some hypotheses not being generalized correctly when using this feature, but I don't think I have the example anymore.

Reid Barton (May 29 2023 at 23:04):

By the way, is the intention for rcases also to use the default induction tactic for the type it's analyzing?

Reid Barton (May 29 2023 at 23:04):

rcases/rintro

Yury G. Kudryashov (May 29 2023 at 23:15):

Should I try to add

@[eliminator]
def Nat.rec' {α :   Sort _} (zero : α 0) (step :  n, α n  α (n + 1)) (n : ) : α n :=
  Nat.rec zero step n

and see how it works?

Yury G. Kudryashov (May 29 2023 at 23:15):

If yes, how should I name the assumptions? zero/step, zero/succ, base/step?

Mario Carneiro (May 29 2023 at 23:15):

I'm pretty skeptical of custom eliminators, they were pretty buggy in lean 3

Mario Carneiro (May 29 2023 at 23:15):

zero/succ

Mario Carneiro (May 29 2023 at 23:16):

otherwise lots of things will break

Yury G. Kudryashov (May 29 2023 at 23:16):

We can't know if they work in Lean 4 unless we try.

Mario Carneiro (May 29 2023 at 23:17):

I'm sure lots of things will still break if you put this in an early file, since the proofs will be assuming to see succ and not n+1

Mario Carneiro (May 29 2023 at 23:17):

I'm expecting to see >200 files touched by this refactor

Yury G. Kudryashov (May 29 2023 at 23:17):

At least, I'll need to remove lots of rw [Nat.zero_eq]

Mario Carneiro (May 29 2023 at 23:19):

Mario Carneiro said:

zero/succ

Another reason for this naming is that you won't get hovers on the cases otherwise

Eric Wieser (May 29 2023 at 23:29):

Mario Carneiro said:

I'm pretty skeptical of custom eliminators, they were pretty buggy in lean 3

They work reliably in lean 4 via induction using

Mario Carneiro (May 29 2023 at 23:30):

in particular I am concerned about (1) the eliminator not looking sufficiently eliminator-like for some code path, (2) the type being eliminated not actually being an inductive or not having the same constructors and code assuming that it is basically the same as the eliminator

Mario Carneiro (May 29 2023 at 23:31):

there is still quite a lot baked into the language regarding handling of inductive types

Mario Carneiro (May 29 2023 at 23:31):

we are a long way from "view patterns"

Mario Carneiro (May 29 2023 at 23:32):

re: rcases, I don't know. rcases just calls the internal cases method so you should be able to see whether it does anything with default eliminators

Yury G. Kudryashov (May 29 2023 at 23:37):

I just want to call induction n and see 0 (not Nat.zero) and n + 1.

Yury G. Kudryashov (May 29 2023 at 23:37):

Or it can break other tactics?

Mario Carneiro (May 29 2023 at 23:42):

Yury G. Kudryashov said:

Or it can break other tactics?

That is what I'm saying. Even if cases and induction themselves work, a lot of things assume that they work in a particular way, and generally only think about inductive types

Mario Carneiro (May 29 2023 at 23:43):

but like you said, the only way to be sure is to try it and find out

Kyle Miller (May 29 2023 at 23:43):

It looks like @[eliminator] might only really affect induction and cases directly

Mario Carneiro (May 29 2023 at 23:43):

at the very least, linters and code actions have to be aware of it

Kyle Miller (May 29 2023 at 23:43):

rcases/rintro use the Lean.MVarId.cases function, which appears not to look at @[eliminator] unless it's not obviously an inductive type

Kyle Miller (May 29 2023 at 23:44):

@Mario Carneiro Everything you're saying seems like an argument to try this out, since anything that breaks is something that should be fixed, and now we have a better shot at fixing these things in Lean 4

Mario Carneiro (May 29 2023 at 23:45):

heck, you can even use @[eliminator] also to do multi-target inductions. I have no idea how this is supposed to be used in practice and I'm sure everything will break

Mario Carneiro (May 29 2023 at 23:47):

match is, as far as I am aware, completely ignorant of @[eliminator]

Mario Carneiro (May 29 2023 at 23:47):

which is unfortunate since that's really where you want to do view-pattern-like things

Mario Carneiro (May 29 2023 at 23:48):

the difficulty level there is higher since you have to do exhaustiveness checking too

Eric Wieser (May 29 2023 at 23:51):

What's languages do have "view patterns"?

Mario Carneiro (May 29 2023 at 23:51):

haskell and scala come to mind

Sebastian Ullrich (May 30 2023 at 08:45):

A lovely paper by my former colleague on how complicated things get in Haskell: https://pp.ipd.kit.edu/uploads/publikationen/graf20lyg.pdf (also has a Lean 3 formalization)

Eric Wieser (May 30 2023 at 08:58):

I had naively assumed that Lean would have prepared me for Haskell's type theory, but then saw

The inhabitants of the type Maybe Void are , Nothing, and (Just ⊥)

which is two more elements than Option Empty has in Lean!

Jireh Loreaux (May 30 2023 at 13:44):

Yeah, in Haskell every type is inhabited by \bot, which is ... special:joy:

François G. Dorais (May 30 2023 at 20:20):

Mario's suspicions are partly right: the eliminator works fine for induction but it doesn't work perfectly well with cases. For example:

@[eliminator] protected def Nat.recAux.{u} {motive : Nat  Sort u} (zero : motive 0) (succ : (n : Nat)  motive n  motive (n+1)) : (t : Nat)  motive t
| 0 => zero
| n+1 => succ n (Nat.recAux zero succ n)

example (n : Nat) : 0 + n = n := by
  induction n with
  | zero =>
    -- ⊢ 0 + 0 = 0
    rfl
  | succ n ih =>
    -- n: Nat
    -- ih: 0 + n = n
    -- ⊢ 0 + (n + 1) = n + 1
    rw [Nat.add_assoc, ih]

example (n : Nat) : 1 - n  1 := by
  cases n with
  | zero =>
    -- ⊢ 1 - 0 = 0
    exact Nat.le_refl ..
  | succ n =>
    -- n: Nat
    -- a✝: n + 1 = n → 1 - (n + 1) ≤ 1 -- what!?
    -- ⊢ 1 + (n + 1) ≤ 1
    rw [Nat.succ_sub_succ, Nat.zero_sub]
    exact Nat.zero_le ..

Personally, I still use the eliminator attribute. However, mostly locally. One use case that I find convenient is this diagonal recursor:

protected def Nat.recDiag.{u} {motive : Nat  Nat  Sort u}
  (zero_zero : motive 0 0)
  (succ_zero : (x : Nat)  motive x 0  motive (x + 1) 0)
  (zero_succ : (y : Nat)  motive 0 y  motive 0 (y + 1))
  (succ_succ : (x y : Nat)  motive x y  motive (x + 1) (y + 1)) :
  (x y : Nat)  motive x y
  | x, 0 => left x
  | 0, y => right y
  | x + 1, y + 1 => succ_succ x y <| Nat.recDiag zero_zero succ_zero zero_succ succ_succ x y
where
  left
  | 0 => zero_zero
  | x+1 => succ_zero x (left x)
  right
  | 0 => zero_zero
  | y+1 => zero_succ y (right y)

For example:

attribute [local eliminator] Nat.recDiag

example (x y : Nat) : y  (y - x) + x := by
  induction x, y with
  | zero_zero => exact Nat.zero_le ..
  | succ_zero x => exact Nat.zero_le ..
  | zero_succ y => exact Nat.le_refl ..
  | succ_succ x y ih =>
    rw [Nat.succ_sub_succ]
    apply Nat.succ_le_succ
    exact ih

Admittedly, for just one or two proofs, it's better to use induction x, y using Nat.recDiag with...

Scott Morrison (Jun 29 2023 at 01:25):

Just cross-referencing everthing: this will need modification in core to get anywhere, but there is a mathlib4 branch where you can see this in action at #5562.

Patrick Massot (Oct 02 2023 at 13:53):

Are there any news about this? I'm teaching induction today and I realized too late that it is still a mess. It would be really really nice to fix this.


Last updated: Dec 20 2023 at 11:08 UTC