Zulip Chat Archive
Stream: lean4
Topic: Subsingleton.elim in a def
Kyle Miller (Mar 02 2023 at 14:54):
I was having an issue where Subsingleton.elim is more powerful than you expect in a def/instance than a theorem:
def foo (β : Sort _) (x y : β) : x = y :=
Subsingleton.elim _ _
Kyle Miller (Mar 02 2023 at 14:54):
What rule is allowing this to go through?
Yaël Dillies (Mar 02 2023 at 14:56):
Can you #print the resulting declaration? I suspect it found Subsingleton Prop and unified the universe variable with 0. All that because the body of defs is inspected for unification.
Kyle Miller (Mar 02 2023 at 14:56):
(I was thinking of leaving it as a puzzle why this isn't totally broken, but the spoiler for that is that Sort _ unifies with Prop. If you change it to Type _ it doesn't use the Subsingleton Prop instance.)
Anne Baanen (Mar 02 2023 at 14:56):
Kyle Miller (Mar 02 2023 at 14:57):
Yes, I know it's instSubsingleton that it's finding. The question is why it's ok with using this instance even though beta is a Sort ?u
Kyle Miller (Mar 02 2023 at 14:59):
This is bad for finite instances, since it can make Sort variables collapse to Prop without you realizing it.
Kyle Miller (Mar 02 2023 at 15:04):
I think it's also not very good how you can't protect against it when there's automation inside proofs inside definitions. Like, imagine that apply Subsingleton.elim is done using a mathlib3-style congr' tactic:
def foo (β : Sort _) (x y : β) : {z // x = z} :=
⟨y, by apply Subsingleton.elim⟩
Eric Wieser (Mar 02 2023 at 15:05):
I think in general a lot of places where we'd write Sort _ in lean3 need to become Sort u in Lean 4
Eric Wieser (Mar 02 2023 at 15:05):
In Lean 4, the universe metavariables in variables lines are now bound per lemma, rather than globally to a free universe variable
Eric Wieser (Mar 02 2023 at 15:06):
Which means you can write variables {I : Sort _}, and still end up with all the lemmas in the file begin about I : Type u without realizing it
Kyle Miller (Mar 02 2023 at 15:10):
Ok, that's a good hint for how to resolve some subtle bugs I ran into when I was modifying convert to use Subsingleton.elim.
There were some instances that weren't being picked up anymore despite nothing seeming to involve convert (spooky action at a distance). Eventually I traced through to find the instance that didn't work, and it took too long to realize that β here specialized to Prop:
instance Equiv.finite_right {α β : Sort _} [Finite β] : Finite (α ≃ β) :=
Finite.of_injective Equiv.toEmbedding fun e₁ e₂ h => Equiv.ext <| by convert FunLike.congr_fun h
Kyle Miller (Mar 02 2023 at 15:10):
Is there any case where you would want a Sort _ variable to specialize to Prop?
Eric Wieser (Mar 02 2023 at 15:11):
I think in general we almost never want universe variable unification of any kind before the colon
Kyle Miller (Mar 02 2023 at 15:12):
It seems like having Sort _ or Type _ specialize to Sort n or Type n with a constant n is never what you want. But I haven't really thought about it.
Eric Wieser (Mar 02 2023 at 15:12):
And in mathlib we just use it as lazy declaration of free universe variables
Eric Wieser (Mar 02 2023 at 15:12):
Having Type _ unify to Type (max u v) is a problem we ran into a bunch in mathlib3, and we have a linter against it (more specifically, against things which use only (max u v) and not either universe variable separately)
Reid Barton (Mar 02 2023 at 17:30):
Eric Wieser said:
And in mathlib we just use it as lazy declaration of free universe variables
Yes, IMO this was always a bad practice.
Reid Barton (Mar 02 2023 at 17:35):
Does Lean 4 have implicit declaration of universe variables?
Kyle Miller (Mar 02 2023 at 17:36):
Yes, autoimplicits applies to universe variables too.
Kyle Miller (Mar 02 2023 at 17:37):
I made use of it in the fix for the issue I was having that led to this thread.
Kyle Miller (Mar 02 2023 at 17:41):
though it doesn't create fresh universe variables for types introduces via autoimplicits. Using the tactic from the linked PR, this unfortunately works:
def foo (x y : α) : x = y := by
congr!
(but thankfully changing def -> theorem keeps it from working)
Eric Wieser (Mar 02 2023 at 17:44):
I think the problem is that people much prefer writing {R M N P Q : Type _} to {R : Type u} {M : Type v} {N : Type w} {P : Type x} {Q : Type y}
Eric Wieser (Mar 02 2023 at 17:45):
It's not {a : Type _} vs {a : Type u} that is the problem
Reid Barton (Mar 02 2023 at 17:46):
Well it's (potentially) a problem if you write Type _ when you mean Type u, but I agree it's not a problem if you know not to do that
Eric Wieser (Mar 02 2023 at 17:46):
Maybe Lean 4 could repurpose Type* as syntax for "generate new universe variables (not metavariables) for everything in this group"?
Eric Wieser (Mar 02 2023 at 17:47):
Is that easy to write as a syntax extension?
Kyle Miller (Mar 02 2023 at 17:47):
Just going back to this last foo example, note that α was introduced using autoimplicits, and apparently this is indistinguishable from doing (α : Sort _).
Reid Barton (Mar 02 2023 at 17:51):
(Unfortunately?) I think this is actually correct/desirable behavior, since it would be pretty annoying if you couldn't use autoimplicit types in combination with e.g. docs4#IO
Kyle Miller (Mar 02 2023 at 17:56):
Yeah, it also lets you introduce propositions:
example : p → q → p := Function.const _
The unfortunate part is just that something like Type* doesn't help with autoimplicit arguments, which have been seeming like a way to get around certain issues with variables. I'm not sure I've seen an example yet of these issues impacting types introduced via variable though, since you can't do induction/cases on them so tactics shouldn't touch them.
Floris van Doorn (Mar 02 2023 at 19:05):
Eric Wieser said:
Maybe Lean 4 could repurpose
Type*as syntax for "generate new universe variables (not metavariables) for everything in this group"?
It's a bit limiting. I think we have plenty of cases in mathlib where we write Type _ for a bunch of types, and some of the universe variables are equal to each other. One example: you're using a category theory hom between types (potentially with extra structure).
Kyle Miller (Mar 29 2023 at 01:08):
Here's another funny example of this:
import Mathlib.Logic.Basic
theorem hmm (a b : α) : decide (a = b) = true := by simp
Yaël Dillies (Mar 29 2023 at 01:36):
Floris, I think the plenty of cases you mention are still not many. Often you only have one type, and almost everywhere away from category theory everything is fully universe-polymorphic.
Last updated: May 02 2025 at 03:31 UTC