Zulip Chat Archive
Stream: new members
Topic: Fully qualified identifiers, and possible "aliases"?
Isak Colboubrani (May 01 2025 at 18:49):
If I can successfully run #check <name>
or #print <name>
, does that mean <name>
is the unique, fully qualified identifier for that theorem/declaration?
Consider the examples below (Polynomial.X_pow_sub_C_ne_zero
and mul_assoc
):
Once I’ve verified that #check Polynomial.X_pow_sub_C_ne_zero
and #print Polynomial.X_pow_sub_C_ne_zero
works, can I be sure there are no other aliases or shorter names for that same theorem (unless, of course, the theorem has been duplicated under a different name)?
Is Polynomial.X_pow_sub_C_ne_zero
itself the fully qualified name—independent of its location (Mathlib.Algebra.Polynomial.Degree.Operations
)?
And how does this apply to more "global" declarations like mul_assoc
? What is the fully qualified name for mul_assoc
?
#mwe:
import Mathlib
#print Polynomial.X_pow_sub_C_ne_zero
#check Polynomial.X_pow_sub_C_ne_zero
-- import Mathlib.Algebra.Polynomial.Degree.Operations"
#print mul_assoc
#check mul_assoc
-- import Mathlib.Algebra.Group.Defs"
Kyle Miller (May 01 2025 at 18:53):
Isak Colboubrani said:
If I can successfully run
#check <name>
or#print <name>
, does that mean<name>
is the unique, fully qualified identifier for that theorem/declaration?
No, that is not the case.
- You might have
open
commands that make it possible to refer to a declaration by a shorter name. - There might be aliases, created using the
export
command.
Simple counterexample:
#check toString
open Nat
#check succ
Kyle Miller (May 01 2025 at 18:54):
Both #check <name>
and #print <name>
will also succeed even if <name>
is ambiguous. They print all interpretations.
Kyle Miller (May 01 2025 at 18:58):
I believe both #check
and #print
will print the fully-qualified names.
You can always hover over the declaration name to be sure. I think that gives the fully qualified name.
Kyle Miller (May 01 2025 at 18:59):
Isak Colboubrani said:
What is the fully qualified name for
mul_assoc
?
mul_assoc
Isak Colboubrani (May 01 2025 at 19:16):
Thanks @Kyle Miller!
Great point about open
—I hadn’t considered that, but it makes perfect sense.
So generally, to find a declaration’s fully qualified name, can I just take the first identifier in the #check <name>
output and ignore the trailing universe annotation (.{u}
)?
In this example, I would treat Polynomial.X_pow_sub_C_ne_zero
as the fully qualified name:
import Mathlib
open Polynomial
#check X_pow_sub_C_ne_zero
-- Polynomial.X_pow_sub_C_ne_zero.{u} {R : Type u} [Ring R]
#check Polynomial.X_pow_sub_C_ne_zero
-- Polynomial.X_pow_sub_C_ne_zero.{u} {R : Type u} [Ring R]
Also, what do the these 32 universe-specific variants mean?
import Mathlib
#check Polynomial.X_pow_sub_C_ne_zero.{0}
-- X_pow_sub_C_ne_zero : 0 < ?m.4 → ∀ (a : ?m.1), X ^ ?m.4 - C a ≠ 0
#check Polynomial.X_pow_sub_C_ne_zero.{1}
-- X_pow_sub_C_ne_zero : 0 < ?m.1973 → ∀ (a : ?m.1970), X ^ ?m.1973 - C a ≠ 0
#check Polynomial.X_pow_sub_C_ne_zero.{2}
-- X_pow_sub_C_ne_zero : 0 < ?m.3942 → ∀ (a : ?m.3939), X ^ ?m.3942 - C a ≠ 0
#check Polynomial.X_pow_sub_C_ne_zero.{32}
-- X_pow_sub_C_ne_zero : 0 < ?m.5911 → ∀ (a : ?m.5908), X ^ ?m.5911 - C a ≠ 0
I naïvely assumed one of them would simply display the same output as for #check Polynomial.X_pow_sub_C_ne_zero
, but they all seem to contain ?m.xxx
placeholders. What’s happening here?
Kyle Miller (May 01 2025 at 19:17):
can I just take the first identifier in the
#check <name>
output
Kyle Miller said:
I believe both
#check
and
Emphasis here on "believe". To be sure, you'll have to dig into the source code yourself.
Kyle Miller (May 01 2025 at 19:19):
Also, what do the these 32 universe-specific variants mean?
I am puzzled by your motivation here. Do you expect the different instantiations of the universe parameters to be fundamentally different from one another?
In any case, take a look at the source for #check
. You'll see why there's different behavior when you give it something that's not an identifier.
Isak Colboubrani (May 01 2025 at 19:35):
Kyle Miller said:
Also, what do the these 32 universe-specific variants mean?
I am puzzled by your motivation here. Do you expect the different instantiations of the universe parameters to be fundamentally different from one another?
Quite the opposite—I assumed they were exactly the same, so I naïvely expected identical output for #check Polynomial.X_pow_sub_C_ne_zero
and #check Polynomial.X_pow_sub_C_ne_zero.{n}
for any . (And if that wasn't the case I expected at least one of them to have exactly the same output as #check Polynomial.X_pow_sub_C_ne_zero
: namely the one at the declaration’s original, lowest universe level.)
(I'm a beginner here, but the expectation above comes from the following: my simple mental mode that each universe embeds into the next—everything in universe 0 also lives in universe 1, everything in universe 1 in universe 2, and so on up to universe 32. As far as I understand, Polynomial.X_pow_sub_C_ne_zero
is a Prop
and thus has it "home" in universe 0 (but can be found in universe 1-32 too!).)
Kyle Miller (May 01 2025 at 19:36):
Given your questions about exact implementations of tactics, I strongly recommend you investigate the implementation of #check
.
Kyle Miller (May 01 2025 at 19:38):
and so on up to universe 32
There are infinitely many universe levels. Passing 32 simply triggers the "you probably don't mean this" error.
Aaron Liu (May 01 2025 at 19:40):
You can pass 31 + n
to exceed it (for concrete n
)
Kyle Miller (May 01 2025 at 19:41):
Isak Colboubrani said:
As far as I understand,
Polynomial.X_pow_sub_C_ne_zero
is aProp
and thus has it "home" in universe 0 (but can be found in universe 1-32 too!)
I'm not sure that's the right interpretation. Yes, this theorem has a type that is in Prop
(a.k.a. Sort 0
), but the domains of the foralls are types, and the theorem is parameterized so that those domains can come from various universe levels.
The Prop
universe is "impredicative". The effect of impredicativity is that, despite the fact that the domains can lie in larger universes, the type is regardless still in Prop
.
Kyle Miller (May 01 2025 at 19:46):
(You give me a :+1: @Aaron Liu, but there's a subtlety that I'm not sure about. I don't know if there are versions of type theory where the way impredicativity works is that the type is "actually" in the higher universe, but then there is a corresponding Prop
that means "the type in that higher universe is inhabited." At least from within Lean's system it's "no, Polynomial.X_pow_sub_C_ne_zero
is not found in any higher universes, it is only in Prop
".)
Aaron Liu (May 01 2025 at 19:53):
Kyle Miller said:
(You give me a :+1: Aaron Liu, but there's a subtlety that I'm not sure about. I don't know if there are versions of type theory where the way impredicativity works is that the type is "actually" in the higher universe, but then there is a corresponding
Prop
that means "the type in that higher universe is inhabited." At least from within Lean's system it's "no,Polynomial.X_pow_sub_C_ne_zero
is not found in any higher universes, it is only inProp
".)
What do you by "corresponding"? Is that a function?
Isak Colboubrani (May 01 2025 at 19:54):
Thanks, that’s really helpful—TIL!
I’m left wondering why Lean displays the fully elaborated signature for #check mul_assoc
, but shows a ?m.1
placeholder when I force it to universe 0 by doing #check mul_assoc.{0}
. Is that due to what you mentioned about the theorem being parameterized?
Is it that at level 0 Lean can no longer infer the parameters (G : Type u₁
, [Semigroup G]
, etc.) when printing?
More specifically, I’m wondering whether this is merely a practical implementation choice, or if Lean "doesn’t have" the required information to print that at universe level 0 (from a theoretical perspective).
import Mathlib
#check mul_assoc
-- mul_assoc.{u_1} {G : Type u_1} [Semigroup G] (a b c : G) : a * b * c = a * (b * c)
#check mul_assoc.{0}
-- mul_assoc : ∀ (a b c : ?m.1), a * b * c = a * (b * c)
Aaron Liu (May 01 2025 at 19:55):
#check
has special support for identifiers vs terms
Aaron Liu (May 01 2025 at 19:56):
So since mul_assoc
is an identifier, it gets the special treatment. You can see what happens when you do #check (mul_assoc)
is different.
Isak Colboubrani (May 01 2025 at 20:02):
Aaron Liu said:
You can pass
31 + n
to exceed it (for concreten
)
Heh, nice trick @Aaron Liu! It seems to work for , but the process can be repeated to reach arbitrarily high:
#check mul_assoc.{32+33}
-- fails: maximum universe level offset threshold (32) has been reached, …
#check mul_assoc.{32+32}
-- works
#check mul_assoc.{32+32+32+32+32+32+32+32+32+32}
-- works
Edward van de Meent (May 01 2025 at 20:03):
perhaps the following is to your satisfaction:
import Mathlib
#check mul_assoc
-- mul_assoc.{u_1} {G : Type u_1} [Semigroup G] (a b c : G) : a * b * c = a * (b * c)
#check (mul_assoc)
-- mul_assoc : ∀ (a b c : ?m.2), a * b * c = a * (b * c)
#check mul_assoc.{0}
-- mul_assoc : ∀ (a b c : ?m.288), a * b * c = a * (b * c)
Aaron Liu (May 01 2025 at 20:05):
Isak Colboubrani said:
Heh, nice trick Aaron Liu!
Of course you can also set_option maxUniverseOffset 999
and get up to 999 offsets.
Edward van de Meent (May 01 2025 at 20:05):
which is to say, there's nothing special about universe levels here, it's just that once you provide specific terms, the #check
command needs to employ a different algorithm for displaying the type
Kyle Miller (May 01 2025 at 21:41):
Isak Colboubrani said:
I’m left wondering why Lean displays the fully elaborated signature for
#check
I see you didn't follow up on my strong suggestion. Please read it — if there is anything you don't understand, please ask followup questions.
Kyle Miller (May 01 2025 at 21:41):
It's all a "go to definition" away
Last updated: May 02 2025 at 03:31 UTC