Zulip Chat Archive

Stream: new members

Topic: Where is sSup defined on submodules?


Kevin Buzzard (Feb 13 2025 at 20:37):

Lean does not do magic, but I can't figure out how it's doing this. In Mathlib.Algebra.Module.Submodule.Lattice we have docs#Submodule.completeLattice which defines a complete lattice structure on the submodules of a module over a (semi)ring. I was asked at Xena where the sSup field is coming from. I cannot figure it out and all the smart people already left for dinner :-/ The declaration in the mathlib file is

instance completeLattice : CompleteLattice (Submodule R M) :=
  { (inferInstance : OrderTop (Submodule R M)),
    (inferInstance : OrderBot (Submodule R M)) with
    sup := fun a b  sInf { x | a  x  b  x }
    le_sup_left := fun _ _  le_sInf' fun _ h, _⟩  h
    le_sup_right := fun _ _  le_sInf' fun _ ⟨_, h  h
    sup_le := fun _ _ _ h₁ h₂  sInf_le' h₁, h₂
    inf := (·  ·)
    le_inf := fun _ _ _  Set.subset_inter
    inf_le_left := fun _ _  Set.inter_subset_left
    inf_le_right := fun _ _  Set.inter_subset_right
    le_sSup := fun _ _ hs  le_sInf' fun _ hq  by exact hq _ hs
    sSup_le := fun _ _ hs  sInf_le' hs
    le_sInf := fun _ _  le_sInf'
    sInf_le := fun _ _  sInf_le' }

Before this instance, sSup doesn't work on sets of submodules. Afterwards, it does. But neither OrderTop nor OrderBot supply it and it's not in the list of explicit fields either. I know the trick mathematically -- I can even see it in #print completeLattice -- there is a sInf defined by this point on submodules (before the completeLattice instance), and the sSup of S is just the sInf of all the submodules which contain every element of S. But where in mathlib is this code and why is it running?

Edward van de Meent (Feb 13 2025 at 20:59):

i think this might be unification at work?

Riccardo Brasca (Feb 13 2025 at 20:59):

There is Submodule.instInfSet that seems relevant

Edward van de Meent (Feb 13 2025 at 21:00):

commenting out the last four fields and replacing them with sorry seems to make it complain it doesn't know what sSup is

Edward van de Meent (Feb 13 2025 at 21:02):

specifically, it looks like the value of the sSup_le field is what allows it to infer the value of sSup?

Edward van de Meent (Feb 13 2025 at 21:04):

i do agree, this behaviour seems quite magical

Etienne Marion (Feb 13 2025 at 21:09):

I agree with Edward, sSup_le (and le_sSup also works) allow to infer the definition of sSup. This seems to rely on the Set _ vs _ → Prop definitional equality so that the hypothesis hs is interpreted as a belonging to the set of submodules containing all elements of s without making this set explicit, via sInf_le'.

Edward van de Meent (Feb 13 2025 at 21:11):

actually, replacing sSup_le with sorry makes the le_sSup field error, so i'm not sure that it also works?

Edward van de Meent (Feb 13 2025 at 21:14):

it will complain that it doesn't know hq : x✝ ∈ ?m.39162 x✝² is a function. It is after lean is able to fill in the metavariable (because membership of an intersection is defeq to a forall statement), but not before.

Etienne Marion (Feb 13 2025 at 21:14):

You’re right my bad

Riccardo Brasca (Feb 13 2025 at 21:21):

Indeed one can do

instance completeLattice : CompleteLattice (Submodule R M) :=
  { (inferInstance : OrderTop (Submodule R M)),
    (inferInstance : OrderBot (Submodule R M)) with
    sup := fun a b  sInf { x | a  x  b  x }
    le_sup_left := fun _ _  le_sInf' fun _ h, _⟩  h
    le_sup_right := fun _ _  le_sInf' fun _ ⟨_, h  h
    sup_le := fun _ _ _ h₁ h₂  sInf_le' h₁, h₂
    inf := (·  ·)
    le_inf := fun _ _ _  Set.subset_inter
    inf_le_left := fun _ _  Set.inter_subset_left
    inf_le_right := fun _ _  Set.inter_subset_right
    le_sSup := fun _ _ hs  le_top --note the new proof
    sSup_le := sorry
    le_sInf := fun _ _  le_sInf'
    sInf_le := fun _ _  sInf_le' }

example {R M : Type} [CommRing R] [AddCommGroup M] [Module R M] (S : Set (Submodule R M)) :
  sSup S =  := rfl

Edward van de Meent (Feb 13 2025 at 21:24):

right, so it does try to unify at le_sSup, but the "correct" value only allows unification after sSup_le is unified

Anatole Dedecker (Feb 13 2025 at 21:27):

In any case, I think we would be very happy with a PR actually adding the definition...

Riccardo Brasca (Feb 13 2025 at 21:29):

Yes, I even think a linter saying something would be a good thing. Maybe forcing to write at least sSup := _ (that works just fine), to flag that this is find by unification (maybe the actual definition is very long and it can be useful to not write it explicitly). Not writing anything just seems confusing.

Edward van de Meent (Feb 13 2025 at 21:41):

#21850 adds an explicit value to the field

Kevin Buzzard (Feb 14 2025 at 09:15):

Wait so what is going on here? Can I not define le when making a partial order as long as one of the proofs makes it clear what it must be? Is this a general phenomenon? I had no idea that you could just randomly skip fields and that unification would attempt to fill them in

Junyan Xu (Feb 14 2025 at 09:25):

The { ..., sup := ... } is probably just a syntax sugar for an application of the function CompleteLattice.mk, so you can omit some fields just like you can use underscores for some arguments if they can be inferred.

Kevin Buzzard (Feb 14 2025 at 09:37):

Yes but IIRC docs#CompleteLattice.mk asks for SupSet to be found via typeclass inference which was what confused me (edit: I'm not at Lean right now and the docs won't let me check this)

Riccardo Brasca (Feb 14 2025 at 09:37):

What I find confusing in this example is that Lean infers data from a proof. I understand why this works, but it goes against the slogan "proofs don't matter", and it looks to me code smell.

Kevin Buzzard (Feb 14 2025 at 09:38):

Looks like it's fixed now though :-)

Kevin Buzzard (Feb 14 2025 at 09:40):

I would say I still don't understand why it works. I am amazed that unification is allowed at all in this setting

Riccardo Brasca (Feb 14 2025 at 09:45):

It's like

theorem foo (a : Nat) (ha : a + 0 = a) : a = 37 := by sorry

example : False := by
  have := foo _ (Nat.add_zero 55)
  sorry

Riccardo Brasca (Feb 14 2025 at 09:45):

Since Nat.add_zero 55 proves 55+0=55 Lean understands that the have is 55 = 37.

Kevin Buzzard (Feb 14 2025 at 09:51):

But where is the underscore in the sSup example? For me it's the underscore which means "use unification". All I could find was "use typeclass inference". If you leave out the definition of a field of a structure lean doesn't use unification to fill it in AFAIK, it gives an error (is this where I'm wrong?)

Riccardo Brasca (Feb 14 2025 at 09:54):

Ah yes, that is some magic in the where syntax. My proposal is indeed to at least force people to write sSup := _ to make it clear that it is found by unification

Kevin Buzzard (Feb 14 2025 at 09:57):

I'm not sure what you're proposing to change here: Just to be clear, master now has the field filled in.

The experiment I want to do: make a structure with two fields: a nat and a proof it's 37. Then instantiate the structure by giving the proof as rfl but not giving the nat at all. Does this work?

Riccardo Brasca (Feb 14 2025 at 09:58):

Yes, this specific example is fixed. I would like the previous syntax to give an error

Riccardo Brasca (Feb 14 2025 at 09:58):

Going to teach now

Yaël Dillies (Feb 14 2025 at 10:03):

Kevin Buzzard said:

The experiment I want to do: make a structure with two fields: a nat and a proof it's 37. Then instantiate the structure by giving the proof as rfl but not giving the nat at all. Does this work?

Yes it does. I use this trick all the time to fill in the default arguments to docs#BooleanAlgebra and similar

Kevin Buzzard (Feb 14 2025 at 10:06):

import Mathlib

structure foo where
  a : 
  ha : a = 37

example : foo where
  ha := rfl

Edward van de Meent (Feb 14 2025 at 13:12):

Riccardo Brasca said:

Ah yes, that is some magic in the where syntax. My proposal is indeed to at least force people to write sSup := _ to make it clear that it is found by unification

it's not just where, it evidently also works with the {foo := _} notation

Kyle Miller (Feb 15 2025 at 22:54):

(where is a macro for {...} notation)

Kyle Miller (Feb 15 2025 at 23:04):

I'm working on some improvements to {...} notation, so some changes here could be possible. (Yes, this so-called 'structure instance notation' is sort of sugar for the structure's constructor @Junyan Xu, though it's a bit more complicated because it knows about default values and overriding default values, and, soon, filling in default values using all parent instances. Also, it doesn't expand as a macro to the structure constructor. It processes the fields and constructs the constructor application expression itself.)

While it's surprising that sup was filled in using the type of a proof, I'm not sure exactly what could be done here to get {...} to detect that this happened... I could imagine perhaps detecting which fields are Prop-valued and elaborating those last. Or perhaps we disallow filling in fields by pure unification — i.e., we throw a warning when a field isn't given and there is no default value for it. You'd have to write sup := _ if you wanted to opt-in to the current behavior.

Junyan Xu (Feb 16 2025 at 01:39):

Riccardo Brasca said:

What I find confusing in this example is that Lean infers data from a proof. I understand why this works, but it goes against the slogan "proofs don't matter", and it looks to me code smell.

I think it infers the data from the type of the proof. It's dependent type at work :)

Edward van de Meent (Feb 16 2025 at 09:27):

Kyle Miller said:

(where is a macro for {...} notation)

yes, so it's the where semantics that is magic, not the where syntax.
clearly {...} is different syntax.

Kyle Miller (Feb 16 2025 at 09:42):

I'm not sure what you're pointing out. Earlier you mentioned that it works with {...} notation too, but that's what I'd expect since where is a macro for it.

Edward van de Meent (Feb 16 2025 at 09:50):

i'm pointing out that you Riccardo Brasca said it's the where syntax that is magic (emphasis mine). if that were the case, it wouldn't work with {...} since it's different syntax with the same semantics.

Kyle Miller (Feb 16 2025 at 09:55):

I'm confused, I haven't said anything was magic. Are you confusing me with Riccardo?

Edward van de Meent (Feb 16 2025 at 10:24):

Oops :grimacing: my mistake :upside_down:

Riccardo Brasca (Feb 16 2025 at 10:33):

Kyle Miller said:

You'd have to write sup := _ if you wanted to opt-in to the current behavior.

This is what I proposed.

Riccardo Brasca (Feb 16 2025 at 10:36):

I understand it's not magic, and that it works because Lean sees the type of the proof so it guesses the Prop. But the fact the this was not immediately clear to expert users suggests it is a confusing behavior. (but maybe it's just our fault :sweat_smile: )

Kyle Miller (Feb 16 2025 at 10:47):

This is what I proposed.

Thanks for the confirmation, I wasn't completely sure what the proposal was, but I see it now in #new members > Where is sSup defined on submodules? @ 💬.

I think this will happen within the next couple releases.

Kyle Miller (Feb 16 2025 at 11:02):

Concretely, explicit fields will be required (unless they have a default value), implicit fields will be optional.

Kyle Miller (Mar 30 2025 at 21:54):

Kevin Buzzard said:

structure foo where
  a : 
  ha : a = 37

example : foo where
  ha := rfl

This is what this example now reports post lean4#7717:

fields missing: 'a'

field 'a' must be explicitly provided, its synthesized value is
  37

If you instead define

 structure foo where
  {a : Nat}
  ha : a = 37

then it does not complain about the missing field.

Mario Carneiro (Mar 30 2025 at 22:12):

can you do

example : foo where
  a := _
  ha := rfl

?

Kyle Miller (Mar 30 2025 at 22:34):

Yeah, that's allowed

Edward van de Meent (Mar 31 2025 at 11:51):

i'm curious, were there other definitions in mathlib which had to be changed because of this?

Edward van de Meent (Mar 31 2025 at 11:54):

(find out at this diff)

Eric Wieser (Mar 31 2025 at 12:00):

I think that diff is everything so in 4.19.0, not just the effects of the relevant PR

Edward van de Meent (Mar 31 2025 at 12:02):

:sad:

Eric Wieser (Mar 31 2025 at 12:04):

I think this diff is the one you want

Kevin Buzzard (Mar 31 2025 at 15:01):

Oh this looks like a really happy ending to this story!


Last updated: May 02 2025 at 03:31 UTC