Zulip Chat Archive
Stream: new members
Topic: difference between `Type*` and `Type _`?
llllvvuu (Jul 10 2024 at 05:13):
Is there any difference between Type* and Type _? (besides Type* requires mathlib)
Kyle Miller (Jul 10 2024 at 05:14):
Type* creates a fresh universe level variable u and elaborates to Type u, and Type _ creates a fresh universe level metavariable ?u and elaborates to Type ?u.
Kyle Miller (Jul 10 2024 at 05:15):
If you have (X Y : Type*) then for example X = Y would be a type error (they have different universe level variables), but (X Y : Type _) would allow X = Y since the universe level metavariables can unify.
Kyle Miller (Jul 10 2024 at 05:17):
(note that (a b : ty) elaborates as (a : ty) (b : ty), with ty elaborated twice)
llllvvuu (Jul 10 2024 at 16:28):
Kyle Miller said:
If you have
(X Y : Type*)then for exampleX = Ywould be a type error (they have different universe level variables), but(X Y : Type _)would allowX = Ysince the universe level metavariables can unify.
Thanks for the explanation!
Julian Berman (Jul 10 2024 at 16:57):
(This may not be true anymore, or may be controversial, so I'll say it anyway so that someone disagrees if it isn't) but I think @Eric Wieser told me to generally avoid Type _ unless you're really sure you want that behavior, because it can cause Lean to do some expensive unification (in the way Kyle mentioned).
Matthew Ballard (Jul 10 2024 at 16:59):
If Type* fails and Type _ is ok, then you probably have secret universe constraints floating around
Matthew Ballard (Jul 10 2024 at 17:00):
You will only see it for type synonyms now
Julian Berman (Jul 10 2024 at 17:01):
But in the case that both work, Type _ can end up being lots slower as Lean tries to find some potentially non-trivial minimal universes to satisfy all the holes right?
Eric Wieser (Jul 10 2024 at 17:01):
When Type* succeeds you should always use it over Type _
Eric Wieser (Jul 10 2024 at 17:02):
It doesn't succeed on type synonym def MyNat : Type* := Nat, where Type _ is ok.
Eric Wieser (Jul 10 2024 at 17:02):
Everywhere else, probably you should work out what the _ is being inferred as and write that instead
Matthew Ballard (Jul 10 2024 at 17:03):
Yes, generally you should be explicit about levels anyway. They get introduced automatically (autoimplicit) still and can have a dramatic effect on the performance as Lean needs to do an additional solve for each step
Matthew Ballard (Jul 10 2024 at 17:05):
Even though it probably yields no benefit peformance-wise, I would still try to avoid Type _ in synonyms. That way everyone who reads the code knows where everything live explicitly. This is just my opinion though
Julian Berman (Jul 10 2024 at 17:06):
Can you (either of you) explain what's special about type synonyms?
Julian Berman (Jul 10 2024 at 17:06):
Is it because Nat itself is defined using Type _?
Matthew Ballard (Jul 10 2024 at 17:06):
API boundaries
Matthew Ballard (Jul 10 2024 at 17:06):
Oh wait. Sorry. I thought you meant their use.
Julian Berman (Jul 10 2024 at 17:06):
Ah I see, so you're saying "use the one you find from the thing I'm aliasing"?
Julian Berman (Jul 10 2024 at 17:07):
No I understand what they're for yeah, just not why Type _ is then something needed
Matthew Ballard (Jul 10 2024 at 17:07):
Here you have a secret constraint. The level of the thing after := better be the same as the level of the synonym
Julian Berman (Jul 10 2024 at 17:07):
Got it, right, makes sense, thanks.
Eric Wieser (Jul 10 2024 at 17:08):
Matthew Ballard said:
Even though it probably yields no benefit peformance-wise, I would still try to avoid
Type _in synonyms. That way everyone who reads the code knows where everything live explicitly. This is just my opinion though
I think the argument is probably that no one really cares about the universe; but people certainly want to see def foo : Type _ := maybeAType instead of def foo := maybeAType
Matthew Ballard (Jul 10 2024 at 17:08):
I think that is also the reason why levels can be introduced implicitly still
Matthew Ballard (Jul 10 2024 at 17:08):
eg [Category C]
Kyle Miller (Jul 10 2024 at 17:09):
@Julian Berman Beyond performance, an additional concern with Type _ is that your theorems might be more specialized than you think.
-- "All types are subsingletons"
example (α : Sort _) (x y : α) :
letI : Subsingleton α := inferInstance
x = y :=
Subsingleton.elim _ _
-- Oh, no they're not.
example (α : Sort*) (x y : α) :
letI : Subsingleton α := inferInstance -- fails
x = y :=
Subsingleton.elim _ _
Kyle Miller (Jul 10 2024 at 17:09):
This was the primary motivation for Type*/Sort*
Matthew Ballard (Jul 10 2024 at 17:10):
After they got introduced, at least one thing was found to live in Type 0 for example
Julian Berman (Jul 10 2024 at 17:11):
And what's happening there (and I'll butcher the terminology I'm sure, sorry), is that while Lean is elaborating the actual type after the :, it's doing that before it works out the _ in the binder, and so once it gets to that it picks a narrower _ than it "should"?
Kyle Miller (Jul 10 2024 at 17:11):
If you change that example to a def and #print it you'll see
Kyle Miller (Jul 10 2024 at 17:12):
Or maybe even move your cursor right before x = y to see what instance it picked up in the local context
Matthew Ballard (Jul 10 2024 at 17:12):
Except I don't think there is a "should". It fills in what works without telling you what choices it makes do so
Kyle Miller (Jul 10 2024 at 17:13):
There's a "should" in the sense that when you write α : Sort _ you wrote it because you thought it would make it be more polymorphic than it ends up being
Julian Berman (Jul 10 2024 at 17:13):
Right I meant in ^ sense that I still don't really understand when this behavior is so desirable.
Julian Berman (Jul 10 2024 at 17:14):
It seems like a footgun.
Kyle Miller (Jul 10 2024 at 17:14):
Exactly, that's why we made Type*/Sort*
Matthew Ballard (Jul 10 2024 at 17:14):
As Eric said, the counterpoint is that is it is a pain to write
universe a b c d ... A B C ... a1 b1 c1 ...
Kyle Miller (Jul 10 2024 at 17:14):
The desirability of Type _ is that _ is a plain old universe level placeholder (which elaborates to a fresh plain old universe level metavariable). That is, it's just a combination of basic features.
Kyle Miller (Jul 10 2024 at 17:15):
and there's no mechanism in Lean to make a metavariable that isn't unifiable with a constant, or whatever the rule is supposed to be where it's not a footgun
Last updated: May 02 2025 at 03:31 UTC