Zulip Chat Archive
Stream: mathlib4
Topic: bug in revert
Jireh Loreaux (Jul 10 2024 at 17:53):
I think there's a bug in revert
:
import Mathlib.Data.Fintype.Card
example {E n : Type*} [Fintype n] {T : n → E} : ∀ j, T j = T j := by
revert T
have : True := trivial -- comment this line to break the proof *and* the statement elaboration
refine Fintype.induction_subsingleton_or_nontrivial n ?base ?step
case base => intros; rfl
case step => intros; rfl
Jireh Loreaux (Jul 10 2024 at 18:00):
With set_option pp.raw true
, before the have
the goal state is:
forall {T : _uniq.23 -> _uniq.22} (j : _uniq.23), Eq.{succ u_1} _uniq.22 (T j) (T j)
and after it is:
[mdata noImplicitLambda:1 forall {T : _uniq.23 -> _uniq.22} (j : _uniq.23), Eq.{succ u_1} _uniq.22 (T j) (T j)]
Jireh Loreaux (Jul 10 2024 at 18:02):
I've posted this in the Mathlib stream instead of lean4 because I haven't been able to minimize away the particular induction lemma used, and so I'm not sure it's a problem with revert
.
Thomas Murrills (Jul 10 2024 at 18:17):
Some minimization:
import Lean
@[elab_as_elim]
def Foo.induction {P : (α : Type) → Prop} (α : Type) : P α := sorry
example {n : Type} : ∀ {T : n}, T = T := by
have : True := trivial -- comment this line to break the proof
exact Foo.induction n
Thomas Murrills (Jul 10 2024 at 18:20):
It seems like elab_as_elim
is essential here; the proof breaks completely either way without it.
(Though interestingly, without @[elab_as_elim]
the error reporting location still changes depending on whether the no_implicit_lambda
mdata is present! It makes sense that the error itself is different, but I'm surprised the location is different too, given that they're both type mismatches on Foo.induction n
. Maybe mdata isn't handled correctly when reporting (these) errors?)
Jireh Loreaux (Jul 10 2024 at 18:22):
Do you mind posting this in the lean 4 stream?
Floris van Doorn (Jul 10 2024 at 18:23):
and/or open an issue in the lean4 repository
Jireh Loreaux (Jul 10 2024 at 18:29):
Note that providing the motive explicitly works though (even with by exact
). Maybe this is unsurprising, but I felt it was worth noting.
import Lean
@[elab_as_elim]
def Foo.induction {P : (α : Type) → Prop} (α : Type) : P α := sorry
example {n : Type} : ∀ {T : n}, T = T := by
have : True := trivial -- comment this line to break the proof
exact Foo.induction n
example {n : Type} : ∀ {T : n}, T = T :=
@Foo.induction (fun n : Type => ∀ {T : n}, T = T) n
Thomas Murrills (Jul 10 2024 at 18:41):
Ah, sorry, short on time right now; maybe someone with the power to do so could move this entire thread to lean4 though?
Thomas Murrills (Jul 10 2024 at 18:42):
Note though that providing the motive explicitly actually reverses the behavior wrt no_implicit_lambda
!
example {n : Type} : ∀ {T : n}, T = T := by
-- have : True := trivial -- *un*comment this line to break the proof
exact Foo.induction (P := fun n : Type => ∀ {T : n}, T = T) n
Kyle Miller (Jul 10 2024 at 21:07):
The implicit lambda feature seems like it might be a red herring (edit: not exactly, it's related, but there's still another bug). This causes a typechecking bug on each example
:
@[elab_as_elim]
def Foo.induction {P : (α : Type) → Prop} (α : Type) : P α := sorry
example {n : Type} {T : n} : T = T := by
exact Foo.induction n
example {n : Type} {T : n} : T = T := Foo.induction n
Kyle Miller (Jul 10 2024 at 21:11):
This seems to be a failure to have elabAsElim report "motive not type correct" here. This is what it's trying to pass off as a valid term:
example {n : Type} {T : n} : T = T := @Foo.induction (fun x ↦ @Eq x T T) n
Jireh Loreaux (Jul 10 2024 at 21:19):
That doesn't explain why the have
fixes computing the motive.
Kyle Miller (Jul 10 2024 at 21:20):
That's true, but it does mostly explain why there's an error on example
rather than on exact
Kyle Miller (Jul 10 2024 at 21:33):
The reason why have
fixes it does turn out to be the implicit lambda feature. It's simply that the motive can only be type correct if you don't introduce the T
argument first. Both of these work:
example {n : Type} : {T : n} → T = T := no_implicit_lambda% (Foo.induction n)
example {n : Type} : (T : n) → T = T := Foo.induction n
Kyle Miller (Jul 10 2024 at 21:36):
Here's a workaround for your original example:
import Mathlib.Data.Fintype.Card
example {E n : Type*} [Fintype n] {T : n → E} : ∀ j, T j = T j := by
revert T
change ∀ T, _ -- changes binder type from implicit to explicit
refine Fintype.induction_subsingleton_or_nontrivial n ?base ?step
case base => intros; rfl
case step => intros; rfl
Kyle Miller (Jul 10 2024 at 21:36):
It seems worth filing an issue that revert
doesn't give you a way to change binders from implicit to explicit and it interacts badly with the implicit lambda feature.
Kyle Miller (Jul 10 2024 at 21:37):
I would have suggested using the induction
tactic here, but that doesn't seem to work when n
is a type like this.
Eric Wieser (Jul 10 2024 at 21:38):
I think there is an issue somewhere about induction
not working for docs#Fintype.induction_subsingleton_or_nontrivial ?
Eric Wieser (Jul 10 2024 at 21:40):
The issue is the implicit arguments I believe, not the type
Eric Wieser (Jul 10 2024 at 21:41):
Here is the thread
Eric Wieser (Jul 10 2024 at 21:42):
Thomas Murrills (Jul 10 2024 at 21:42):
Why do (or maybe a better question, why should) some tactics, like have
, wind up with no_implicit_lambda
on the goal, and some don’t? It’s hard to see the rhyme and reason.
Thomas Murrills (Jul 10 2024 at 21:43):
Is it essentially just leftover from its role as a necessary internal flag?
Kyle Miller (Jul 10 2024 at 21:46):
lean4#4722 has the elabAsElim fixes, which make Foo.induction
fail in the right place.
Thomas Murrills (Jul 10 2024 at 21:47):
And more generally: should no_implicit_lambda
ever appear on a user-facing goal? (Or should it effectively always appear on the goal?)
I’m just wondering if it’s reasonable to have something that invisibly changes behavior appear inconsistently like that.
Kyle Miller (Jul 10 2024 at 21:47):
I think for have
the point is to make sure that have
doesn't also do an intro
for implicits that happen to be there.
Kyle Miller (Jul 10 2024 at 21:48):
I've been thinking that it might make more sense if the implicit lambda feature is activated at by
and then disabled in some consistent way from then on...
Kyle Miller (Jul 10 2024 at 22:14):
Here's another workaround @Jireh Loreaux, I forgot about this notation:
example {E n : Type*} [Fintype n] {T : n → E} : ∀ j, T j = T j := by
revert T
refine @(Fintype.induction_subsingleton_or_nontrivial n ?base ?step)
case base => intros; rfl
case step => intros; rfl
Jireh Loreaux (Jul 10 2024 at 22:23):
I'm not sure what that new @
notation means
Kyle Miller (Jul 10 2024 at 22:26):
@(e)
means "disable implicit lambda feature when elaborating e
"
Last updated: May 02 2025 at 03:31 UTC