-
Notifications
You must be signed in to change notification settings - Fork 34
RFC: namespaces #25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
RFC: namespaces #25
Conversation
I've got to say that I really like the idea of separating, at least in part, the notion of namespace from the notion of modules (some may remember me championing this sort of idea years ago). On the other hand, this proposal still feels a bit half-baked. And I would guess this is because the motivations are a bit narrow. (also let me remark that namespace and mixins are fairly different concepts, I would advise against equating them in the proposal). Let me share some of my own thoughts as a way forward:
I'm probably missing other good motivations, so I'll come back here when I have more. |
Yes, I miss this.
+1. I don't know well Haskell but I can see how Agda is much more flexible than Coq in structuring name spaces. To be able replicate to the directory structure within a file is also something to achieve. What syntax do we need for these features? For instance, would it be enough, as proposed in BZ#3171 to extend the |
I think this CEP is too important to be stalled like that for 3 years. Who wants to have a working group about it? |
Why not. This is indeed important. |
Count me in. |
I'd be interested in joining |
I'm happy to join. Who is leading? (I mean, pinpointing the problems/differences... it may help to jump start the discussion). |
I could lead somehow, I had a look at Lean namespaces and agda modules recently. We also led a small experiment with Enrico recently (LPCIC/coq-elpi#177). Should we use a coq call slot or another slot? We could perhaps meet September Wednesday 2nd or Thursday 3rd? |
My own feeling is that a slot different than the call slot might be more comfortable (specific audience, different time constraints) but that's only my own view. Wednesday or Thursday are fine to me. A related question in passing: in PR rocq-prover/rocq#12890, there is a need to generate names morally private to a tactic. Would it make sense as part of the discussion to consider private name spaces, or private name subspaces where it is ensured that no collision with user names will happen, e.g., here for ring, a subspace like |
Is the idea of associating a name space to inductive types part of the proposal? It is in the air for long (and finally something of that sort is implemented in Lean), but it seems to me that it would be rather natural to have Somehow, this is also starting to give more structure to the identifiers, observing that |
I'm rather an outsider these days, but for the record I'd very much like that. |
I think the discussion should separate the mechanism and it's adoption in the common practice. I do like the idea of inductive as namespaces, and the derive command in Elpi already hacks it with modules, see But what is more important to me are the details of the mechanism. For example the last line of Imo we should focus on these details first. |
Let's try to meet: https://framadate.org/MmlNR3wImPHtDLqX |
The best date is Thursday, September 10, 2020 - 16:00-17:00 CEST. Let's meet then. @JasonGross @gares @herbelin does https://visio.inria.fr work for you all? |
I think visio works---that's what Coq calls use, right? |
I just sent @JasonGross @herbelin and @gares an invitation by mail |
@CohenCyril can I get one as well? |
sure, wait a sec |
Here are my notes:
|
Should namespaces be global entities, always visible, or be contained in modules? (I can open a namespace N defined in a module A only after having imported A). To my recollection, namespaces are global in all the PL I've used. |
Ah I see, however, it cannot be the case for functors, first you do not have a symbol before you apply it, and you may not want to automatically populate a name space by a mere application (e.g. |
I think you do. As soon as a new named symbol is created, it has to live in a namespace. I don't see the point of delaying the existence of this name. (Note that populating a namespace might be a lazy operation, but that is just an implementation detail. It should not be observable by the user.)
For the record, C++ has the notion of anonymous namespaces. Symbols in such namespaces can only be accessed by the current translation unit. In Coq parlance, it means that they have no fully qualified name, or rather, their fully qualified name is random. |
If we decorrelate modules and namespaces, it means
And these seem to be two distinct primitives, which make sense to combine together for users of the |
For me, these two cases are the same primitive ("create a given name in a given namespace"), but it can indeed be called numerous times: once for creating the module name There is, however, another primitive for creating the namespace |
Not obvious to me, I think the opposite actually, namespace x exist iff there is a definition which prefix is x. |
This looks like a footgun: Module A. End A.
Import Namespace A. (* Module A is empty, and so is the namespace. Should it fail because the namespace does not exist? *)
Import Namespace A'. (* A' is a typo. Should it succeed? *) |
If we are talking about namespaces and not modules anymore, doing |
To be more precise, there is a difference between Namespace A. End A.
Open Namespace A. (* raises a warning "empty namespace" *)
Open Namespace A'. (* warning "empty namespace" *) And Module A. End A. (* assuming this create both the module and name `A` in the root namespace *)
Open Namespace A. (* warning "empty namespace" *)
Open Namespace A'. (* warning "empty namespace" *)
Import A. (* no warning, usual behavior *)
Import A'. (* error: no module A in the root namespace *) |
I'm still a bit puzzled JustModule A. (* the low level primitive *) (* \____ Module A *)
Namespace A. (* / *)
Definition x.
End Namespace A.
JustEnd A. When one writes To me it is Do we all agree on that? |
cf16d0e
to
7df4fd8
Compare
In case it wasn't brought up: Namespaces should also differ regarding (super)global effects —
|
With a 3-years hindsight, has someone an idea of why it stalled? Is it that the design space is still too large to start an experimentation? E.g. why not starting stepwise, e.g. by reusing the existing implementation of modules and adding a tag to modules to tell which of them have to be considered as name spaces? Then adding gradually features to these "namespaces"? |
Has this stalled? IMHO, Elpi's Namespace Emulation System (NES) lets you try the feature today, so there's no need for a gradual migration — we know what works and doesn't work, and implementing that experience into core Coq would avoid the encoding artifacts we see today. We already use NES at Bedrock and have contributed improvements; NES is already adequate for evaluation. General downsides of a gradual migration on the Coq side:
OTOH, supporting gradual migration of clients is welcome, and often necessary. |
Assume we implement the following:
Would that fit the needs? Compared to Elpi emulation, that would give a proper official global name to the namespace instead of implicit wrapper names of the form Compared to what @gares wrote above:
This would give two names to a same thing: the (unshadowable) physical name
As advocated by @Blaisorblade, side effects (or at least selectable side effects) would be possible. |
An alternative to having both a "physical" unshadowable name and a "namespace" shadowable name is to have only an unshadowable "namespace" name (and then the kernel name is forged on the namespace name). This means that loading two distinct modules populating the same namespace with a same name is rejected. I guess that this typically assumes namespaces to be registered to a central place: e.g. for a namespace living under a library |
This breaks many invariants we have today, but I don't get what it would bring. |
Let's explore the idea that the kernel name of a declaration in a namespace, say, [Added: what I describe below does not seem right: the extension of a namespace should be "stored" in the library file being built. So, that should rather be like an ordinary module except that 1) the prefix for naming the contents of the module is different from the one of the module 2) the module should not be considered a member of its own surrounding module/library. Regarding the We would also need a table of namespace (as a summary but also in the kernel so that we can rebuild a module out of its components dispatched in different files).] Trying to prepare an implementation, this means, at the time of the command
At the time of the
At the time of loading a
Other remarks:
|
So, my previous proposal not being right, here is another (simpler) proposal for shadowing-free name spaces:
@gares: does this correspond to what you have in mind? Does it satisfy the invariants you have in mind? To implement name spaces allowing shadowing, use instead the "physical" kernel name as handle in the global |
Summary of Coq call (11 Nov 2023): @CohenCyril and @gares think that the ability to shadow is important. There are however different views on what the status of shadowed names should be. As far as I could understand:
Maybe a compromise would be to allow referring to a shadow name when working interactively, but not in a document intended to serve as a library? |
What about to reserve a namespace root for kernel names, e.g. In a sense it would not be a real namespace, but just a way to use a Kernel Name to address a constant by its, well, kernel name. The current kernel invariants will ensure there is no collision there, and the pretty printer can always fall back to that namespace if needed.
It may be important to be able to "resurrect" a shadowed concept, but I think it is also important to discourage this. |
Why not.
This is also how I see it.
We could also provide a command |
Maybe something like other languages do |
It requires a syntax change, and |
Actually, I'm still confused about shadowing. In the long discussion above and in particular in this comment, only shadowing of partially qualified names is mentioned, but this is already supported since the fully qualified handle on the name is not hidden. Still browsing the discussion above, the motivation for shadowing (and actually even of simply removing from the scope) seems only to discourage users to use, or even see, a name that is not part of the intended abstraction. But this is not a question different from the question of abstraction we already have with modules and I believe it can be treated separately (*). So, is there really a typical case where shadowing a fully qualified name would be needed, that is, is there a typical case where two files define the same absolute name, say (*) e.g. with module types as they are now, or with lighter, more implicit ways, to specify module types. A second question is: @gares, @CohenCyril, how do you imagine a namespace to be initiated? E.g. to declare a name space, say, |
If we keep the current syntax for denoting things, eg Similar collision may arise by importing two libraries which independently add the same name in the same namespace, eg |
If we don't keep the same syntax (which I don't like, but helps explaining), then |
Since we want the new mechanism to have the nice, usual, syntax, it was proposed to use the ugly one for kernel names, and I proposed another way to skin the cat by saying that |
Indeed, there is the underlying question of whether names of namespaces live in the same space or not as names of modules. I was assuming that they would live in the same name space, i.e. that Unfortunately, I don't know how to solve the question. What would be the pros and cons of each approach? |
I think that I really see this as separating two existing concepts, and giving access to both independently. As I was saying the nametab already does roughly what we need, but there is no way to operate on it without also creating a container or opening it. But I think I'm just repeating my summary up there. |
I see. Then, the question of whether every But first, how do you define, say, a I'm now also a bit confused about the objective to provide a naming layout which is not necessarily the same as the one of files. Letting every file, and every module of a file be a namespace, etc., seems to compete with the objective to discourage users to refer to names which we may want to keep internal. So, it seems that an arbitration between the different objectives is needed. |
Just compiling the current files will give you a set of name spaces which is identical to the current module structure. No magic. But one can change that in a library and take advantage of the new feature. I let @CohenCyril give examples about MC. |
@JasonGross feel free to take over this PR