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 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.

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