Zulip Chat Archive
Stream: general
Topic: universe issue with `Type*`
Kevin Buzzard (Feb 27 2021 at 18:26):
There was a time when I was scared of Type*
, preferring to roll my own universes. However there were changes, particularly the advent of {A B C : Type*}
, and I switched back. But here's something which I think is odd:
import linear_algebra.basis
open submodule
open_locale big_operators classical
open finsupp
universe u
set_option pp.universes true
lemma submodule.span_as_sum {R : Type*} {M : Type*} [semiring R] [add_comm_group M] [semimodule R M]
{m : M} {s : set M} (hm : m ∈ submodule.span R s) :
∃ c : M →₀ R, (c.support : set M) ⊆ s ∧ (c.sum (λ i, (smul_add_hom R M).flip i)) = m :=
begin
refine span_induction hm (λ x hx, _) ⟨0, by simp⟩ _ _; clear hm m,
{ refine ⟨finsupp.single x 1, λ y hy, _, by simp⟩,
rw [finset.mem_coe, mem_support_single] at hy,
rwa hy.1 },
{ rintros x y ⟨c, hc, rfl⟩ ⟨d, hd, rfl⟩,
refine ⟨c + d, _, by simp⟩,
refine set.subset.trans _ (set.union_subset hc hd),
rw [← finset.coe_union, finset.coe_subset],
exact support_add },
{ rintros r m ⟨c, hc, rfl⟩,
refine ⟨r • c, λ x hx, hc _, _⟩,
{ rw [finset.mem_coe, mem_support_iff] at hx ⊢,
rw [finsupp.coe_smul] at hx,
exact right_ne_zero_of_mul hx },
{ rw sum_smul_index' (λ (m : M), _),
{ convert (add_monoid_hom.map_finsupp_sum (smul_add_hom R M r) _ _).symm,
ext m s,
simp [mul_smul r s m] },
{ exact (((smul_add_hom R M).flip) m).map_zero } } }
end
#check @submodule.span_as_sum
/-
submodule.span_as_sum.{u_1 u_2 u_3} :
∀ {R : Type u_1} {M : Type (max u_2 u_3)} [_inst_1 : semiring.{u_1} R] [_inst_2 : add_comm_group.{(max u_2 u_3)} M]
[_inst_3 : semimodule.{u_1 (max u_2 u_3)} R M] {m : M} {s : set.{(max u_2 u_3)} M},
m ∈ span.{u_1 (max u_2 u_3)} R s →
(∃ (c : M →₀ R),
↑(c.support) ⊆ s ∧ c.sum (λ (i : M), ⇑(⇑((smul_add_hom.{u_1 (max u_2 u_3)} R M).flip) i)) = m)
-/
I have R and M in two different universes, but somehow M's universe has become the max of two unspecified universes which I can't figure out the origin of. Paging through the proof I didn't spot any occurrence of u_2
or u_3
outside max u_2 u_3
. Furthermore if I change {M : Type*}
to {M : Type u}
then the proof compiles just the same and the universes in the goal are what I would expect -- one for R and one for M and that's it.
What's going on?
Kevin Buzzard (Feb 27 2021 at 18:34):
Oh it's not even the proof -- if I sorry the proof I still observe the same phenomenon.
Kevin Buzzard (Feb 27 2021 at 18:51):
If I change (λ i, (smul_add_hom R M).flip i)
to (λ m r, r • m)
the issue disappears
Kevin Buzzard (Feb 27 2021 at 18:52):
Got it -- it's add_monoid_hom.flip
.
Damiano Testa (Feb 27 2021 at 18:56):
I am still confused by this. Is there an explicit argument that smul_add_hom
wants that is not there, then?
Damiano Testa (Feb 27 2021 at 18:56):
Should there be an extra R
somewhere?
Eric Wieser (Feb 27 2021 at 19:05):
docs#add_monoid_hom.flip for reference
Kevin Buzzard (Feb 27 2021 at 19:05):
OK so here is some behaviour I find counterintuitive:
import algebra.module.basic
variables {R : Type*} {M : Type*} [semiring R] [add_comm_group M] [semimodule R M]
def a : R →+ M →+ M := smul_add_hom R M
#check @a -- Π {R : Type u_3} {M : Type u_4}
def b {R : Type*} {M : Type*} [semiring R] [add_comm_group M] [semimodule R M]
: M →+ R →+ M := (smul_add_hom R M).flip
#check @b -- Π {R : Type u_3} {M : Type (max u_4 u_5)} ...
def c : M →+ R →+ M := (smul_add_hom R M).flip
#check @c -- Π {R : Type u_3} {M : Type u_4} ...
def d : M →+ R →+ M := a.flip
/-
type mismatch at application
a.flip
term
a
has type
?m_1 →+ ?m_2 →+ ?m_2 : Type (max ? ?)
but is expected to have type
?m_1 →+ ?m_2 →+ ?m_3 : Type (max ? ? ?)
Additional information:
/home/buzzard/active-lean-projects/mathlib/scratch/scratch5.lean: context: switched to simple application elaboration procedure because failed to use expected type to elaborate it, error message
type mismatch at application
a.flip
term
a
has type
?m_1 →+ ?m_2 →+ ?m_2 : Type (max ? ?)
but is expected to have type
?m_1 →+ ?m_2 →+ ?m_3 : Type (max ? ? ?)
-/
Eric Wieser (Feb 27 2021 at 19:06):
Type*
in a lemma or definition is adjusted based on its use
Eric Wieser (Feb 27 2021 at 19:07):
Type*
in variables always means "just make another universe"
Eric Wieser (Feb 27 2021 at 19:07):
I've seen this a lot where I write Sort*
in a lemma, but lean has worked out that my typeclasses require Type*
and changed it for me silently
Kevin Buzzard (Feb 27 2021 at 19:11):
variables {R : Type*} {M : Type*}
def a : R → M → M := λ r m, m
#check @a -- Π {R : Type u_3} {M : Type u_4}
def b {R : Type*} {M : Type*} : M → R → M := flip (λ r m, m)
#check @b -- Π {R : Type u_3} {M : Type u_4} ...
So the phenomenon doesn't persist for bare functions.
Kevin Buzzard (Feb 27 2021 at 19:20):
import algebra.module.basic
universes u v w
variables {M : Type u} {N : Type v} {P : Type w}
[add_monoid M] [add_monoid N] [add_comm_monoid P]
#check flip -- (M → N → P) → N → M → P
#check (M → N → P) → N → M → P -- Type (max (max u v w) v u w)
#check add_monoid_hom.flip -- (M →+ N →+ P) → N →+ M →+ P
#check (M →+ N →+ P) → N →+ M →+ P -- Type (max (max u v w) v u w)
This is the issue I think. add_monoid_hom.flip
, and even flip
, produce terms whose types have some pretty crazy universes.
Kevin Buzzard (Feb 27 2021 at 19:21):
I think we need to simp those universe parameters
Kevin Buzzard (Feb 27 2021 at 19:31):
I can't reproduce with normal functions :-/ I'm still wondering if this can be fixed in the sense that one could get a.flip
working.
import algebra.module.basic
universes u v w
structure foo (M : Type u) (N : Type v) (P : Type w) :=
(hom : M → N → P)
variables {M : Type u} {N : Type v} {P : Type w} [has_scalar M N]
def foo.flip (c : foo M N P) : foo N M P := ⟨λ n m, c.hom m n⟩
def a : foo M N N := ⟨λ m n, m • n⟩
#check @a -- Π {M : Type u_1} {N : Type u_2} ...
def b {M N : Type*} [has_scalar M N]: foo N M N := (⟨λ m n, m • n⟩ : foo M N N).flip
#check @b -- Π {M : Type u_1} {N : Type u_2} ...
Mario Carneiro (Feb 28 2021 at 09:08):
This is usually the fault of a bad definition, which has two universe arguments and only uses max u v
, like you found. But looking at add_monoid_hom.flip
I don't see anything suspicious, it uses exactly as many universes as one would expect.
EDIT: Actually the issue is add_monoid_hom.flip
, or more specifically the unification problem that arises when you match an argument of type R →+ M →+ M
against the input to add_monoid_hom.flip
, because this basically looks like
@add_monoid_hom.{?l_1 ?l_2} (@add_monoid_hom.{?l_2 ?l_2} M M)
vs
@add_monoid_hom.{?l_3 (max ?l_4 ?l_5)} ?m_6 (@add_monoid_hom.{?l_4 ?l_5} ?m_7 ?m_8))
which results in unifying ?l_2
against max ?l_4 ?l_5
, resulting in two free universe metavariables that are later turned into real variables at the end.
The fix for this is simple: Use explicit universes in this particular theorem. This is an issue with the universe unification strategy, maybe it can be fixed, maybe not, but I still think it is best to let the algorithm handle universe variables where possible, in spite of these rare issues
Kevin Buzzard (Feb 28 2021 at 09:14):
So you're saying that I should just not worry about this and use Type u
instead of Type*
for the original theorem? That's fine by me! It's also kind of telling me to go back to using Type u in general.
Mario Carneiro (Feb 28 2021 at 09:16):
It is my impression that this doesn't happen very often
Mario Carneiro (Feb 28 2021 at 09:17):
I think if you use variables {R M : Type*}
then it will use fresh universe variables, instead of metavariables, which are not susceptible to these spurious unification issues
Mario Carneiro (Feb 28 2021 at 09:18):
Arguably this is more natural default behavior
Mario Carneiro (Feb 28 2021 at 09:20):
I'm not sure which of these two is more expected:
variables {A B : Type*}
example : A = B := sorry -- fail
example {A B : Type*} : A = B := sorry -- ok
Damiano Testa (Feb 28 2021 at 09:56):
Thanks! At the moment, the theorem is stated with the M
universe explicit.
Kevin Buzzard (Feb 28 2021 at 12:11):
If you use variables you can go back to Type*, if you care
Damiano Testa (Feb 28 2021 at 12:13):
Given that this lemma went in the middle of a file, where there were other conventions about variables
, I think that I prefer to leave it as is. Had it been on a file that I started with this purpose in mind, I maybe would not have realized, since I might indeed had R
and M
declared as variables at the beginning.
Nick Scheel (Feb 28 2021 at 15:17):
I didn’t quite follow: was the problem determined to be that there were two universe variables that only appeared together as a max u v
but never separately?
I think this situation is bound to come up, but if the type theory is parametric over universes it is okay to solve them to be the same variable. (The one I am studying has this property, so I will do that.) But I think in Lean, because you have LEM and things like ordinals and cardinals which change depending on their universe, it is no longer parametric? Correct me if I am wrong.
Nick Scheel (Feb 28 2021 at 15:21):
Or was the problem that the parts that should have determined the variables were also implicit?
Kevin Buzzard (Feb 28 2021 at 16:01):
The problem was that an equation which one has to solve using universes was solved in a suboptimal way if you ask the system to solve it in a certain way
Kevin Buzzard (Mar 11 2021 at 18:58):
Is this related?
Right now on master
import topology.algebra.group
set_option pp.universes true
/-
topological_group.of_nhds_one.{u_1 u_2} :
∀ {G : Type (max u_1 u_2)} [_inst_5 : group.{(max u_1 u_2)} G] [_inst_6 : topological_space.{(max u_1 u_2)} G],
filter.tendsto.{(max u_1 u_2) (max u_1 u_2)}
(function.uncurry.{(max u_1 u_2) (max u_1 u_2) (max u_1 u_2)} has_mul.mul.{(max u_1 u_2)})
((nhds.{(max u_1 u_2)} 1).prod (nhds.{(max u_1 u_2)} 1))
(nhds.{(max u_1 u_2)} 1) →
filter.tendsto.{(max u_1 u_2) (max u_1 u_2)} (λ (x : G), x⁻¹) (nhds.{(max u_1 u_2)} 1)
(nhds.{(max u_1 u_2)} 1) →
(∀ (x₀ : G),
nhds.{(max u_1 u_2)} x₀ =
filter.map.{(max u_1 u_2) (max u_1 u_2)} (λ (x : G), x₀ * x) (nhds.{(max u_1 u_2)} 1)) →
(∀ (x₀ : G),
filter.tendsto.{(max u_1 u_2) (max u_1 u_2)} (λ (x : G), x₀ * x * x₀⁻¹) (nhds.{(max u_1 u_2)} 1)
(nhds.{(max u_1 u_2)} 1)) →
topological_group.{(max u_1 u_2)} G
All Messages (1)
-/
Changing Type*
to Type u
in #6647 fixes this issue.
Mario Carneiro (Mar 11 2021 at 19:05):
yep
Mario Carneiro (Mar 11 2021 at 19:06):
I think it's tendsto (uncurry ((*) : G → G → G))
that causes the bad unify here
Bryan Gin-ge Chen (Mar 11 2021 at 22:33):
I've put #6647 on the queue. We should add a library note for this if we find some more examples.
Kevin Buzzard (Mar 12 2021 at 06:30):
It was flip
before IIRC, which is of a similar nature to uncurry.
Mario Carneiro (Mar 12 2021 at 14:16):
I want to point my finger at expressions like G → G → G
, where the type of the implication is something like imax u (imax u u)
where u
is the sort of G
. That simplifies to u
when it's a variable, but when that expression appears in stages, where ?u
is a metavariable, it's not obvious that it's going to normalize at first. When it is G → H → G
when the universes are actually different there is no issue because lean guesses correctly.
Last updated: Dec 20 2023 at 11:08 UTC