Zulip Chat Archive

Stream: mathlib4

Topic: Introducing all dependent variables


Kyle Miller (Mar 28 2023 at 18:34):

Picking back up some discussion from while ago, this morning I was looking at how to automatically introduce all the dependencies you need to introduce a typeclass, or really anything that depends on a typeclass.

Kyle Miller (Mar 28 2023 at 18:34):

I've just done a few tests, but it looks promising:

section
variable! [Module R M]
/-
R: Type ?u.18467
M: Type ?u.18470
inst✝²: Semiring R
inst✝¹: AddCommMonoid M
inst✝: Module R M
-/
end

section
variable! [Algebra R A]
/-
R: Type ?u.18767
A: Type ?u.18770
inst✝²: CommSemiring R
inst✝¹: Semiring A
inst✝: Algebra R A
-/
end

section
variable! [Module R M] [Module S M'] (σ : R →+* S) (f : M →ₛₗ[σ] M')
/-
R: Type ?u.20902
M: Type ?u.20905
S: Type ?u.20932
M': Type ?u.20935
inst✝⁵: Semiring R
inst✝⁴: AddCommMonoid M
inst✝³: Module R M
inst✝²: Semiring S
inst✝¹: AddCommMonoid M'
inst✝: Module S M'
σ: R →+* S
f: M →ₛₗ[σ] M'
-/
end

Kyle Miller (Mar 28 2023 at 18:34):

code

(v2 below)

Kevin Buzzard (Mar 28 2023 at 18:36):

Can it do unique factorization domains? :-) (I ask because I had no clue how to say "let R be a unique factorization domain" until a few weeks ago)

Kyle Miller (Mar 28 2023 at 18:38):

@Kevin Buzzard I think you'll have to give me a hint, since I'm at least a few weeks behind you there

Kevin Buzzard (Mar 28 2023 at 19:25):

My understanding is that right now you have to say variables (R : Type) [comm_ring R] [is_domain R] [unique_factorization_monoid R]

Kyle Miller (Mar 28 2023 at 19:56):

Ah, for that we could have a fake typeclass that implies those typeclasses and which doesn't itself appear in the final variables list.

section
-- Imagine this is marked in some way as being an alias for `variables!` purposes
class UniqueFactorizationDomain (R : Type _) [CommRing R] [IsDomain R] [UniqueFactorizationMonoid R]

variable! [UniqueFactorizationDomain R]
/-
R: Type ?u.26397
inst✝³: CommRing R
inst✝²: IsDomain R
inst✝¹: UniqueFactorizationMonoid R
inst✝: UniqueFactorizationDomain R -- imagine it doesn't include this
-/
end

Kyle Miller (Mar 28 2023 at 19:57):

Here's a version that seems to be more robust. I was having a hard time juggling the pending metavariables and getting into the right local contexts, so instead this keeps inserting missing variables and re-elaborating until there are no more failures.

code v2

(edit: v3 below)

Kyle Miller (Mar 28 2023 at 19:58):

There's a limitation with this approach, though, where it doesn't do any computations to see whether some typeclasses imply others

section
variable! (σ : R →+* S) [Module R M] [Module S M']
/-
R: Type ?u.24671
S: Type ?u.24674
M: Type ?u.24695
M': Type ?u.24727
inst✝⁷: NonAssocSemiring R
inst✝⁶: NonAssocSemiring S
σ: R →+* S
inst✝⁵: Semiring R
inst✝⁴: AddCommMonoid M
inst✝³: Module R M
inst✝²: Semiring S
inst✝¹: AddCommMonoid M'
inst✝: Module S M'
-/
end

Kevin Buzzard (Mar 28 2023 at 20:01):

oh man, I want to change the defaults to make everything a comm_ring. Would that be possible? Heh, maybe I'm just asking for variable :-)

Kyle Miller (Mar 28 2023 at 20:04):

Putting some CommRing instances first definitely fixes this last problem:

section
variable! [CommRing R] [CommRing S] (σ : R →+* S) [Module R M] [Module S M']
/-
R: Type ?u.30154
S: Type ?u.30157
M: Type ?u.30320
M': Type ?u.30490
inst✝⁵: CommRing R
inst✝⁴: CommRing S
σ: R →+* S
inst✝³: AddCommMonoid M
inst✝²: Module R M
inst✝¹: AddCommMonoid M'
inst✝: Module S M'
-/
end

Kyle Miller (Mar 28 2023 at 20:05):

I could imagine there being a configuration option where you can give rules like "if you need a NonAssocSemiring, add a CommRing instead"

Thomas Murrills (Mar 28 2023 at 20:07):

Really nice! For the syntax, I wonder: should it use [[]] (or whatever brackets we end up using for that) instead of variable!? E.g. variable [[Module R M]]? This would be consistent with doing the same thing inside defs and theorems. Or is that impractical for some reason?

Kyle Miller (Mar 28 2023 at 20:08):

This has wider scope than a [[...]] binder:

variable! (σ : R →+* S)
/-
R: Type ?u.30490
S: Type ?u.30493
inst✝¹: NonAssocSemiring R
inst✝: NonAssocSemiring S
σ: R →+* S
-/

Kyle Miller (Mar 28 2023 at 20:08):

Also, we can't define our own binders, unfortunately.

Thomas Murrills (Mar 28 2023 at 20:09):

Ah, it also introduces dependencies for non-typeclass variables, got it!

Thomas Murrills (Mar 28 2023 at 20:10):

Kyle Miller said:

Also, we can't define our own binders, unfortunately.

Oh, that is unfortunate…!

Kevin Buzzard (Mar 28 2023 at 20:11):

But I want AddCommGroups! This is why I realise that I should probably just stick to variable. How about analysis? Does [normed_space 𝕜 E] fill in the right gaps? What about [normed_space ℝ E]?

Kevin Buzzard (Mar 28 2023 at 20:13):

What about differential geometry? How much more do I have to give than [smooth_manifold_with_corners I M]?

Kyle Miller (Mar 28 2023 at 20:13):

It doesn't seem that NormedSpace is ported yet.

Kyle Miller (Mar 28 2023 at 20:14):

What's your objection about AddCommGroup?

Kevin Buzzard (Mar 28 2023 at 20:14):

your M was a monoid.

Kyle Miller (Mar 28 2023 at 20:15):

Then you can add that it's an additive commutative group:

variable! [AddCommGroup M] [Module R M]
/-
M: Type ?u.31767
R: Type ?u.31770
inst✝²: AddCommGroup M
inst✝¹: Semiring R
inst✝: Module R M
-/

Kevin Buzzard (Mar 28 2023 at 20:16):

But now R isn't a commutative ring -- what I'm saying is that I'm now understanding that actually I'd be better off with variable because tyipcally I do not want the minimal assumptions for which module makes sense (in fact I never want these assumptions)

Kevin Buzzard (Mar 28 2023 at 20:17):

Of course, others might.

Kyle Miller (Mar 28 2023 at 20:17):

Maybe we don't want variable! in mathlib code, but it at least shows you everything you need to address on your way to a complete variable line.

Kyle Miller (Mar 28 2023 at 20:17):

You can use variable! everywhere variable already works.

Thomas Murrills (Mar 28 2023 at 20:18):

But also R was explicitly given as a commutative ring in the first example but not the second.

Thomas Murrills (Mar 28 2023 at 20:19):

@Kevin Buzzard maybe you want that sort of abbreviation typeclass mentioned earlier to use in place of Module here (which would add all the assumptions you do want)?

Kyle Miller (Mar 28 2023 at 20:20):

There could be configuration options to help you deal with specific situations, like a commutative algebra setting where whenever there is a request for something CommRing implies then use CommRing, etc.

Kevin Buzzard (Mar 28 2023 at 20:21):

I don't really know what I want, I'm just wondering where these ideas are going. For me I always want R to be a commutative ring and M an R-module but I would imagine that the non-commutative guys always want R to be a ring. If semiring people actually exist (something I'm slightly skeptical about) then they'll want something else. I don't have a clear idea about how to support all these competing "canonical" assumptions.

Kevin Buzzard (Mar 28 2023 at 20:21):

Aah, yeah maybe switch on "commutative algebra mode" and then try it :-)

Kyle Miller (Mar 28 2023 at 20:21):

it could be variable! (config := commAlg) ... for you

Kyle Miller (Mar 28 2023 at 20:24):

Another example to show possible scope (introducing Decidable assumptions):

variable! (p : Nat  Prop) (h :  k, p k) (h : 0 < Nat.find h)
/-
p: ℕ → Prop
h✝: ∃ k, p k
inst✝: DecidablePred fun n => p n
h: 0 < Nat.find h✝
-/

Eric Wieser (Mar 28 2023 at 20:27):

Isn't the answer to Kevin's requrest just to adapt your

-- Imagine this is marked in some way as being an alias for `variables!` purposes
class UniqueFactorizationDomain (R : Type _) [CommRing R] [IsDomain R] [UniqueFactorizationMonoid R]

example above, but for VectorSpace?

Kevin Buzzard (Mar 28 2023 at 20:29):

Do we actually want to make this class?

Kevin Buzzard (Mar 28 2023 at 20:29):

I mean, it would be handy for maths undergraduates but would it clog up the system somehow? I remember trying to make vector space just to mean "module over a field" and then this was reverted later because it somehow caused trouble. (I have a poor understanding of these sorts of issues)

Kyle Miller (Mar 28 2023 at 20:35):

Kevin Buzzard said:

Do we actually want to make this class?

Imagine classes that are used only for variables! and which would otherwise show a warning if you try to use them, and where they don't actually appear after they get expanded.

Eric Wieser (Mar 28 2023 at 20:35):

I don't think we want to make a class, but I think we could make it a macro of some kind

Eric Wieser (Mar 28 2023 at 20:35):

I don't think we want to make a class, but I think we could make it a macro of some kind (edit: bad train internet)

Eric Wieser (Mar 28 2023 at 20:37):

Presumably we can override the parser for theorem in mathlib to handle [[foo X]] or [!foo X] or similar so that we're not limited to variables lines

Thomas Murrills (Mar 28 2023 at 20:41):

(Off topic, but I do like e.g. ![foo X] or [foo X]! better than [[foo X]]; all those square brackets are a bit busy imo :) )

Thomas Murrills (Mar 28 2023 at 20:43):

The other option is of course introducing an analogous theorem!

Eric Wieser (Mar 28 2023 at 20:44):

![foo X] might conflict with vector notation; if not to Lean, then perhaps still to the reader

Kyle Miller (Mar 28 2023 at 21:34):

Eric Wieser said:

Isn't the answer to Kevin's requrest just to adapt your [...]

That's one answer, but it might be nice to be able to write variable! (σ : R →+* S) and have it introduce CommRings instead of NonAssocSemirings, if things are in commutative algebraist mode. The class alias idea is just for packaging up some common notions, when it's not obvious what you're supposed to introduce.

One reason using class for this is convenient (rather than creating macros) is that you get things like documentation on mouseover for free, and it'd be easier for non-macro-ologists to write their own. You also can see that your alias is type correct, which you don't get with macros. Still, I wonder if there are examples where macros would be more flexible.

Adam Topaz (Mar 28 2023 at 21:38):

Eric Wieser said:

Presumably we can override the parser for theorem in mathlib to handle [[foo X]] or [!foo X] or similar so that we're not limited to variables lines

Ideally we would find some way to do this under binders as well... What if I want to write forall R S [[Module R S]], ...?

Kyle Miller (Mar 28 2023 at 21:40):

Maybe more generally forall (! σ : R →+* S), ... and forall R S [! Module R S], ...

Adam Topaz (Mar 28 2023 at 21:40):

Is that possible? I thought Mario said it was not on some other thread.

Kyle Miller (Mar 28 2023 at 21:41):

I mean, forall R S [[Module R S]], ... isn't possible either, right?

Adam Topaz (Mar 28 2023 at 21:41):

right

Adam Topaz (Mar 28 2023 at 21:41):

that's why I'm asking the expert :)

Kyle Miller (Mar 28 2023 at 21:42):

I'm just dreaming. Maybe one day Lean 4 devs will create hooks for our own binder types.

Eric Wieser (Mar 28 2023 at 22:39):

Adam Topaz said:

Ideally we would find some way to do this under binders as well... What if I want to write forall R S [[Module R S]], ...?

That feels like a very niche case where it probably pays to be explicit

Adam Topaz (Mar 28 2023 at 22:40):

But this is a general idea... somehow [[Module R S]] should expand to [Semiring R] [AddMonoid S] [Module R S] in all places where this can apply.

Adam Topaz (Mar 28 2023 at 22:41):

And it's not too niche. For example it's certainly reasonable to imagine something like this being used in the definition of some universal property or something.

Adam Topaz (Mar 28 2023 at 22:41):

I suppose if we had such a general feature, we would end up using a lot more mixins, making it even more common.

Eric Wieser (Mar 28 2023 at 22:44):

For example it's certainly reasonable to imagine something like this being used in the definition of some universal property or something.

I would claim this probably happens no more than 10 times in mathlib, but maybe I'm not familiar with the parts that do this

Adam Topaz (Mar 28 2023 at 22:47):

maybe it happens so few times because it's not convenient to do (yet)?

Adam Topaz (Mar 28 2023 at 22:49):

It certainly was not convenient in lean3 not just because of the topic of this thread, but also because the instances didn't register (without using introsI and friends). But now that's been fixed in lean4, and I can imagine it becoming more commonplace.

Kyle Miller (Mar 28 2023 at 22:50):

What's the argument here Eric? I'm not sure why it matters if it's a niche use -- if we had the ability to make our own binders that seems like the right way to implement this feature everywhere.

Eric Wieser (Mar 28 2023 at 22:52):

Mainly just that custom binders in theorem/def/variable is probably possible to do without core changes, and that the benefit of supporting forall on top of that is pretty marginal.

Eric Rodriguez (Mar 28 2023 at 22:55):

Kyle Miller said:

it could be variable! (config := commAlg) ... for you

i wonder if this should be file-wide

Kyle Miller (Mar 28 2023 at 22:55):

Ok, I think saying forall is a niche use is completely independent of whether in the short term (i.e., before the hypothetical time when Lean supports custom binders) we might have custom theorem/def/variables for mathlib that support additional binder types. The only reason why it would matter is if someone were to suggest making a custom forall too.

Kyle Miller (Mar 28 2023 at 23:00):

Here's a v3, now with "Try this" and hopefully more robust binder insertion. Example:

variable!? [Algebra R A]
-- Try this: variable [CommSemiring R] [Semiring A] [Algebra R A]

code v3

Kyle Miller (Mar 28 2023 at 23:03):

I spent a while trying to track down a bug in what I thought was my own code, but I guess variable has the same bug.

This doesn't report any errors:

variable [Semiring R] [AddCommMonoid M] [Module R M] [Semiring S]
   [AddCommMonoid M'] [Module S M'] (f : M →ₛₗ[σ] M')

This does:

variable [Semiring R] [AddCommMonoid M] [Module R M] [Semiring S]
   [AddCommMonoid M'] [Module S M'] (f : M →ₛₗ[σ] M')
#check 1
/-
Error on f's type:
typeclass instance problem is stuck, it is often due to metavariables
  Module ?m.34167 M'
-/

Mario Carneiro (Mar 29 2023 at 00:40):

Kyle Miller said:

Ok, I think saying forall is a niche use is completely independent of whether in the short term (i.e., before the hypothetical time when Lean supports custom binders) we might have custom theorem/def/variables for mathlib that support additional binder types. The only reason why it would matter is if someone were to suggest making a custom forall too.

I am generally dubious of approaches that require replacing theorem/def etc. I think there will be a lot of unintended side effects of doing that across mathlib, e.g. some stuff hard coded in the server looking for a certain syntax kind, syntax kinds being referred to in mathport (or choice nodes!)

Johan Commelin (Mar 29 2023 at 02:49):

@Kyle Miller Is the problem that you didn't specify σ, and Lean is telling you this in a somewhat opaque way?

Kyle Miller (Mar 29 2023 at 03:06):

Yeah, that's why there's an error (when it decides to actually report it). The autoimplicit feature adds σ : ?R1 ->*+ ?R2 to the context, and then it tries to infer Module ?R1 M and Module ?R2 M', but it can't make any progress on these (and they definitely don't follow from Module R M or Module S M').

Arthur Paulino (Mar 29 2023 at 03:57):

Can we have this in core? :pray:

Bryan Gin-ge Chen (Aug 25 2025 at 19:34):

@Kyle Miller What would it take to get something like this over the line? [[]] notation came up again in #PR reviews > #28803 refactor: unbundle algebra from `ENormed*`. I guess we can't use that notation exactly, but it seems to me that what you did with variable! here could help somewhat with the readability issues discussed there.

I also note that Sébastien suggested a variable_alias command in #mathlib4 > Normed modules @ 💬 which could be a next step for this.

Kyle Miller (Aug 25 2025 at 20:16):

@Bryan Gin-ge Chen Mathlib's variable? has @[variable_alias]. That's what Sébastien's suggestion is equivalent to, right? There's an example in MathlibTest/Variable.lean

All of @Sébastien Gouëzel's examples in that linked thread work exactly expected:

import Mathlib

section
variable? {k E : Type*} [Module k E]
/-
Try this: variable? {k E : Type*} [Module k E] =>
  {k E : Type*} [Semiring k] [AddCommMonoid E] [Module k E]
-/
end

section
variable? {k E : Type*} [NormedAddCommGroup E] [Module k E]
/-
Try this: variable? {k E : Type*} [NormedAddCommGroup E] [Module k E] =>
  {k E : Type*} [NormedAddCommGroup E] [Semiring k] [Module k E]
-/
end

@[variable_alias]
structure HilbertSpace' (k E : Type*)
  [RCLike k] [SeminormedAddCommGroup E] [InnerProductSpace k E] [CompleteSpace E]

section
variable? {k E : Type*} [HilbertSpace' k E]
/-
Try this: variable? {k E : Type*} [HilbertSpace' k E] =>
  {k E : Type*} [RCLike k] [SeminormedAddCommGroup E]
  [InnerProductSpace k E] [CompleteSpace E]
-/
end

Note that the "try this" adds in a hint so that the expanded form appears in the source code, and there's a check that it generates the same thing.

If somehow [[...]] is the ideal syntax for "expand only this", I think that could be worked into variable?.

I saw that Sébastien said that a variable_alias command would ideally support hovers — hovering over HilbertSpace' shows all the typeclass assumptions it depends on, because they appear in its type.

Bryan Gin-ge Chen (Aug 25 2025 at 20:28):

Ah, it looks like I totally missed the discussion in #mathlib4 > variable! which is why we aren't already using this all over the place... I guess it may be a good time to revisit this.

Jireh Loreaux (Aug 25 2025 at 20:56):

One issue that isn't addressed with variable? is including this in theorem/def statements.

Bryan Gin-ge Chen (Aug 26 2025 at 17:30):

We'd need to wait for core changes for that, right? In the meantime it's ugly, but variable? ... in theorem does seem to work.

Jireh Loreaux (Aug 26 2025 at 18:30):

Potentially we could make lemma actually different from theorem so that it could parse [[HilbertSpace 𝕜 E]] arguments (or maybe ⟦HilbertSpace 𝕜 E⟧ is a better notation? :shrug:)

Sébastien Gouëzel (Aug 26 2025 at 18:32):

Do I understand correctly that, for now, variable? is not supposed to stay in the code? I.e, we're supposed to replace it with the Try this output, so I don't see how it helps with the verbosity.

Jireh Loreaux (Aug 26 2025 at 18:36):

  1. it may not help with verbosity as-is (but we could potentially make it shut up or introduce a variant to do so).
  2. it would at least help with readability because you can just pay attention to what comes before the =>.

Kyle Miller (Aug 26 2025 at 18:36):

@Sébastien Gouëzel It stays in code. Notice that the "try this" output adds the expanded => clause, but keeps the original input.

Yes, it's verbose, but what came up during review was how for maintainability we'd want to know what the expanded list of instances is. I sort of think it's like a non-terminal simp. The result can be very sensitive to the instance set.

Kyle Miller (Aug 26 2025 at 18:45):

I do want to see double-bracket instance notation one day, but getting it right looks pretty subtle. There have been a few proposals for how to do it too.

It would be nice if there were a solution that wasn't quite so sensitive, or quite so easy to accidentally get some overlapping instances, or that depends on the (slow) process of adding instances on instance synthesis failure. One direction I've been thinking about is having a way to more formally specify the lattice structure on instances. The double bracket notation could look at existing instances and figure out which instances to include to meet the requirements, while avoiding duplicate data fields, and giving errors if it's impossible. I'd hope that you could look at the double bracket notation and figure out what it expands to in your head, or at least trust that it's not doing anything fishy.

The lattice might also be a way to give more specific errors on instance failure in general.

Jireh Loreaux (Aug 26 2025 at 18:48):

I think this might be less of a problem if we unbundled more.

Jireh Loreaux (Aug 26 2025 at 18:51):

Are variable? lines elaborated more than once? I seem to remember that was the case (at least at one point) for variable, but I can't remember if it changed.

Kyle Miller (Aug 26 2025 at 18:53):

The variable? computation happens once, but all the binders it generates get elaborated multiple times, just like variable.


Last updated: Dec 20 2025 at 21:32 UTC