Zulip Chat Archive
Stream: lean4
Topic: typeclass failure
Kevin Buzzard (Jun 02 2021 at 20:52):
I would expect type class inference to succeed here. I couldn't get it to work in Lean 3 either. What am I doing wrong?
class has_note (M : Type) where
note : M
notation "♩" => has_note.note
class has_note_and_foo (M : Type) extends has_note M where
foo : Unit -- in Lean 3 you can just extend has_note with no extra fields and the problem persists
variable {ι : Type} {N : Type} {η : ι → Type} (β : ι → Type)
structure dfinsupp [∀ i, has_note (β i)] : Type :=
(to_fun : ∀ i, β i)
instance dfinsupp.has_note {ι : Type} {β : ι → Type} [∀ i, has_note (β i)] :
has_note (dfinsupp (λ i => β i)) := ⟨⟨λ _ => ♩⟩⟩
instance dfinsupp.has_note_and_foo [∀ i, has_note_and_foo (β i)] :
has_note_and_foo (dfinsupp (λ i => β i)) where
note := ♩
foo := ()
variable (α : Type) (M : Type)
structure finsupp [has_note M] :=
(to_fun : α → M)
instance finsupp.has_note [has_note M] :
has_note (finsupp α M) := ⟨⟨λ i => ♩⟩⟩
instance finsupp.has_note_and_foo [has_note_and_foo M] :
has_note_and_foo (finsupp α M) where
note := ♩
foo := ()
example [has_note_and_foo N] : has_note_and_foo (dfinsupp (λ (i : ι) => finsupp (η i) N)) :=
inferInstance -- fails
example [has_note_and_foo N] : has_note_and_foo (dfinsupp (λ (i : ι) => finsupp (η i) N)) :=
dfinsupp.has_note_and_foo _ -- works
-- in Lean 3, removing the instance attribute from `finsupp.has_note` makes inferInstance work
Supplementary questions which came up when I was making this:
(1) I know it's ridiculous but I didn't know how to make a class foo2 extend foo1 and then add no more fields (so foo2 is a copy of foo1). In Lean 3 you can do class foo2 extends foo1.
.
(2) In Lean 3 I could try and debug using set_option trace.class_instances true
. How do you say that in Lean 4?
(3) In Lean 3 I could experiment with removing instance
tags from my defs with local attribute [-instance] finsupp.has_zero
(which fixes the problem). How do you say that in Lean 4?
PS here is the Lean 3 discussion, with some traces.
Kyle Miller (Jun 02 2021 at 21:50):
Kevin Buzzard said:
(2) In Lean 3 I could try and debug using
set_option trace.class_instances true
. How do you say that in Lean 4?
The equivalent seems to now be set_option trace.Meta.synthInstance true
(3) In Lean 3 I could experiment with removing
instance
tags from my defs withlocal attribute [-instance] finsupp.has_zero
(which fixes the problem). How do you say that in Lean 4?
I don't know if you can remove attributes anymore, but manually removing finsupp.has_note
and including it into finsupp.has_note_and_foo
seems to make the inferInstance
succeed:
--instance finsupp.has_note [has_note M] :
-- has_note (finsupp α M) := ⟨⟨λ i => ♩⟩⟩
instance finsupp.has_note_and_foo [has_note_and_foo M] :
has_note_and_foo (finsupp α M) where
note := ⟨λ i => ♩⟩
foo := ()
Kyle Miller (Jun 02 2021 at 22:34):
I'm not sure why exactly typeclass resolution doesn't work, but somehow splitting off has_foo
and creating a has_note_and_foo
synonym class makes it succeed. (I'm not recommending anything by posting this.)
class has_note (M : Type u) where
note : M
notation "♩" => has_note.note
class has_foo (M : Type u) [has_note M] : Sort where
ax : (♩ : M) = ♩ -- example axiom that has_note should satisfy
class has_note_and_foo (M : Type u) extends has_note M, has_foo M
attribute [instance] has_note_and_foo.mk
structure dfinsupp (β : ι → Type u) [(i : ι) → has_note (β i)] where
to_fun : (i : ι) → β i
instance dfinsupp._has_note {β : ι → Type u} [(i : ι) → has_note (β i)] :
has_note (dfinsupp β) := ⟨⟨λ _ => ♩⟩⟩
instance dfinsupp._has_foo {β : ι → Type u}
[(i : ι) → has_note (β i)] [(i : ι) → has_foo (β i)] :
has_foo (dfinsupp β) where
ax := rfl
structure finsupp (α : Type u) (M : Type v) [has_note M] where
to_fun : α → M
instance finsupp._has_note (M : Type v) [has_note M] :
has_note (finsupp α M) where
note := ⟨λ _ => ♩⟩
instance finsupp._has_foo (M : Type u) [has_note M] [has_foo M] :
has_foo (finsupp α M) where
ax := rfl
example (N : Type u) [has_note N] [has_foo N] (η : ι → Type v):
has_note (dfinsupp (λ (i : ι) => finsupp (η i) N)) :=
inferInstance
example (N : Type u) [has_note N] [has_foo N] (η : ι → Type v):
has_foo (dfinsupp (λ (i : ι) => finsupp (η i) N)) :=
inferInstance
example (N : Type u) [has_note N] [has_foo N] (η : ι → Type v):
has_note_and_foo (dfinsupp (λ (i : ι) => finsupp (η i) N)) :=
inferInstance
Kevin Buzzard (Jun 02 2021 at 22:46):
In the Lean 3 use case, note = 0, foo = +, and finsupp and dfinsupp are finitely supported functions.
Mario Carneiro (Jun 03 2021 at 03:53):
(1) I know it's ridiculous but I didn't know how to make a class foo2 extend foo1 and then add no more fields (so foo2 is a copy of foo1). In Lean 3 you can do
class foo2 extends foo1.
.
That sounds like it could be an issue on the lean 4 repo. I have noticed a number of uses of "1 or more" in the grammar where "0 or more" would be valid, useful and no harder to parse: the fields after where
in this case as well as match x with arms+
come to mind. There are probably some variations of the where
syntax appearing like instance ... where
, def ... where
, as well as let rec
, mutual
, variable
.
Gabriel Ebner (Jun 03 2021 at 07:43):
I think you can do class Foo2 extends Foo1 where
now.
Gabriel Ebner (Jun 03 2021 at 07:44):
Weirdly enough, it's impossible to extend the empty structure:
class Bar
class Foo extends Bar where
-- 'Bar' is not a structure
Mario Carneiro (Jun 03 2021 at 07:45):
oh, class Bar
works?
Mario Carneiro (Jun 03 2021 at 07:46):
I'm surprised it's not class Bar where
Gabriel Ebner (Jun 03 2021 at 07:46):
Yes!
Gabriel Ebner (Jun 03 2021 at 07:47):
Wait, I don't need the where
after extends either. This works:
class Bar where
x : Nat
class Foo extends Bar
Mario Carneiro (Jun 03 2021 at 07:51):
Oh! I had sort of assumed Kevin tried that, since it does seem like the style we want to encourage
Kevin Buzzard (Jun 03 2021 at 07:59):
I am an idiot
Kevin Buzzard (Jun 03 2021 at 08:00):
and indeed inferInstance
is still failing even with foo removal
Sebastien Gouezel (Jun 03 2021 at 17:42):
@Daniel Selsam , do you have an idea why typeclass inference fails on https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/typeclass.20failure/near/241318335 ?
Kevin Buzzard (Jun 03 2021 at 21:14):
Here's the foo-free version:
class has_note (M : Type) where
note : M
notation "♩" => has_note.note
class has_note2 (M : Type) extends has_note M
variable {ι : Type} {N : Type} {η : ι → Type} (β : ι → Type)
structure dfinsupp [∀ i, has_note (β i)] : Type :=
(to_fun : ∀ i, β i)
instance dfinsupp.has_note {ι : Type} {β : ι → Type} [∀ i, has_note (β i)] :
has_note (dfinsupp (λ i => β i)) := ⟨⟨λ _ => ♩⟩⟩
instance dfinsupp.has_note2 [∀ i, has_note2 (β i)] :
has_note2 (dfinsupp (λ i => β i)) where
note := ♩
variable (α : Type) (M : Type)
structure finsupp [has_note M] :=
(to_fun : α → M)
instance finsupp.has_note [has_note M] :
has_note (finsupp α M) := ⟨⟨λ i => ♩⟩⟩
instance finsupp.has_note2 [has_note2 M] :
has_note2 (finsupp α M) where
note := ♩
example [has_note2 N] : has_note2 (dfinsupp (λ (i : ι) => finsupp (η i) N)) :=
inferInstance -- fails
example [has_note2 N] : has_note2 (dfinsupp (λ (i : ι) => finsupp (η i) N)) :=
dfinsupp.has_note2 _ -- works
Kevin Buzzard (Jun 03 2021 at 21:15):
And here's the variant that works -- "remove finsupp.has_note
and bundle it into finsupp.has_note2
":
class has_note (M : Type) where
note : M
notation "♩" => has_note.note
class has_note2 (M : Type) extends has_note M
variable {ι : Type} {N : Type} {η : ι → Type} (β : ι → Type)
structure dfinsupp [∀ i, has_note (β i)] : Type :=
(to_fun : ∀ i, β i)
instance dfinsupp.has_note {ι : Type} {β : ι → Type} [∀ i, has_note (β i)] :
has_note (dfinsupp (λ i => β i)) := ⟨⟨λ _ => ♩⟩⟩
instance dfinsupp.has_note2 [∀ i, has_note2 (β i)] :
has_note2 (dfinsupp (λ i => β i)) where
note := ♩
variable (α : Type) (M : Type)
structure finsupp [has_note M] :=
(to_fun : α → M)
instance finsupp.has_note2 [has_note2 M] : has_note2 (finsupp α M) where
note := ⟨λ i => ♩⟩
example [has_note2 N] : has_note2 (dfinsupp (λ (i : ι) => finsupp (η i) N)) :=
inferInstance -- now works
Mac (Jun 03 2021 at 23:23):
I imagine this has to do with the fact that the original finsupp
has two has_note
instances, one explicit and one it implicitly acquires through its explicit has_note2
instance extending has_note
. Lean might not like these kind of psuedo-diamonds.
Kevin Buzzard (Jun 03 2021 at 23:35):
Those instances are defeq, right? I don't think this is the issue. I think that what you describe happens all over the place in mathlib in lean 3, you make something an additive abelian group and then a ring and this is no problem at all.
Kevin Buzzard (Jun 03 2021 at 23:39):
I think it's something to do with the pi type in the class. You can test your hypothesis by removing the pi type and trying to get type class inference to fail without it but still having your pseudo-diamond.
Mario Carneiro (Jun 04 2021 at 08:41):
set_option trace.Meta.synthInstance true
#synth [has_note2 N] → has_note2 (dfinsupp (λ (i : ι) => finsupp (η i) N))
-- ...
-- [Meta.synthInstance.tryResolve] has_note2
-- (dfinsupp fun (i : ι) => finsupp (η i) N) =?= has_note2 (dfinsupp fun (i : ?m.1404) => ?m.1405 i)
-- [Meta.synthInstance.tryResolve] failure
It appears to be a unification problem involving metavariables under a lambda. It looks like a bug, or at least a regression from lean 3 behavior - here ?m.1405 i
should be unified with finsupp (η i) N
but this is being rejected, and the synthesis fails.
Kevin Buzzard (Jun 04 2021 at 08:46):
It's also failing in lean 3
Kevin Buzzard (Jun 04 2021 at 08:48):
Should I open an issue?
Mario Carneiro (Jun 04 2021 at 08:58):
Hm, if it fails in lean 3 too then that lowers the chance that it will be fixable, but it's certainly something that Daniel or Leo will want to consider, so I would say yes
Kevin Buzzard (Jun 04 2021 at 11:45):
Daniel Selsam (Jun 09 2021 at 16:37):
Sebastien Gouezel said:
Daniel Selsam , do you have an idea why typeclass inference fails on https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/typeclass.20failure/near/241318335 ?
Hey, was away last week. I don't have time to address this now but I took a quick look and my first guess is that it tries to unify: ((?m <something>).1 : has_note2 M) =?= (<something-else> : has_note M)
and that the solution may be to add special support for unification hints for structures with a single field, i.e. making it so that x.1 =?= y
would try x =?= ⟨y⟩
.
François G. Dorais (Jun 09 2021 at 16:42):
Also, it would be nice to have such hints for structures where all but one field is a Prop.
Daniel Selsam (Jun 09 2021 at 16:44):
François G. Dorais said:
Also, it would be nice to have such hints for structures where all but one field is a Prop.
Can you please clarify?
François G. Dorais (Jun 09 2021 at 17:21):
NVM: wrong way around.
Kevin Buzzard (Jun 11 2021 at 18:41):
fixed in lean4#521. Thanks to the devs!
Eric Wieser (Jun 11 2021 at 21:06):
Does that fix actually help for the dfinsupp case? Wasn't add_zero_class
relevant there, which has multiple fields?
Daniel Selsam (Jun 11 2021 at 21:24):
Eric Wieser said:
Does that fix actually help for the dfinsupp case? Wasn't
add_zero_class
relevant there, which has multiple fields?
The example with dfinsupp
posted at https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/typeclass.20failure/near/241455170 works now as well.
Eric Wieser (Jun 11 2021 at 22:39):
Is it likely it would be doable for a lean 3 expert (and obviously not an already busy lean4 dev!) to backport to lean 3? Or does the strategy used to fix this only really exist in lean 4?
Daniel Selsam (Jun 11 2021 at 22:53):
I don't see any obstacle. It is a reasonably local and lightweight change.
Eric Wieser (Aug 10 2021 at 10:13):
(deleted)
Last updated: Dec 20 2023 at 11:08 UTC