-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
what is the top-level scope in a source file, and what names are found there? #1136
Comments
My description of model 3 is: "unqualified lookup finds names in this library; to find exported names from other libraries in this package, qualify them with the name of the package". I do think we want a rule that can be explained clearly and succinctly, do you think my description qualifies? |
I think one of the biggest risks of confusion with model 3 is that each namespace declaration observably introduces two different namespaces -- one in the library and one in the package. For me, your description doesn't sufficiently capture that. Example:
|
Just to be sure, under #3 you mention implicit imports of What would you think of somewhere between (2) and (3), where public names are in the package scope, but there's a subtle library scope for private things? In particular, like (2) names appear in their declared scopes, and like (3) there's a library scope. Unlike (3), the library scope is somewhat separated from the package scope. Name lookup order could be library scope then package scope. Because an To try to explain how that may work:
In your example for rule 3, Regarding the One concern might be that, due to the difference in defaults, a name in the For example:
|
@jonmeow Can you describe how your amended rule would treat namespaces? Specifically for examples like the one in my reply to @josh11b I'd like to be clear on how the package versus library split interacts with the identity of namespaces and how we look for and declare names within them. |
I think there are few options routes... Option (A) would be that i.e.:
Option (B), which I would advocate for, is that library namespaces are disallowed. A namespace only exists as a concept for a package. Libraries are already very limited in scope/conflicts (they are a single i.e.: I think of the namespaces as distinct: i.e., To be explicit in your example:
|
I am totally fine with @jonmeow 's idea that the library scope and
Our current proposed rules are:
|
Yes. This is me looking between options #2 and #3 and thinking that, ergonomically, maybe we should do PackageQualifier = No. However, we could do PackageQualifier = Yes and still do NamespacePrivate = No in order to address zygoloid's problematic namespace example.
I'd suggest instead "LibraryPrivateInNamespace": Are library-private symbols allowed to be in a namespace? (renaming "NamespacePrivate", including in my above response) The reason for this is a separate concept of namespace-private, e.g.:
I'm only mentioning this to note the related concept that may be desirable, but I don't think it should be decided here (although a decision of LibraryPrivateInNamespace = Yes would probably make namespace-private overlap syntactically in bad ways, ruling out the latter feature). |
Hm, I'm not sure we have a great option here, even with NamespacePrivate = No. I can see four possible choices:
|
I assume in your third choice, you're suggesting that all references write Note though, I think it's really the namespace example that pushes me to want to do |
I don't think @jonmeow 's proposal has this problem, because things from other libraries in the same package are visible independent of whether the namespace was declared locally. That is also the downside of PackageQualifier = No. |
Ah, right, yes. If we give up on trying to provide the property that all unqualified names have a local (same file or at least same library) declaration, we can avoid these problems. That property doesn't seem to mesh very well with also wanting a package scope that contains a mixture of local and non-local names. If we want to go that way, I think it'd be simpler to also stop trying to distinguish package, library, and file scopes, and say they're all the same thing: that the top level of a file is its package scope. And separately, private names in a library are private to that library and not visible outside, but that this is accomplished by something more like a linkage and visibility rule rather than by having a distinct scope. With that approach, a |
FWIW, an argument towards injecting Note, a counter-argument for name insertion is that imports from the same package could use some special syntax as well, e.g. |
After reading all of this, I lean toward (2) and as @zygoloid says stopping distinguishing package / library / file scopes. And I somewhat like the idea from @jonmeow of making same-package imports visibly different. I wonder about just omitting the package entirely: |
I find myself also leaning towards rule (2). Specifically and concretely, the rule set I would propose is:
Compared to the above discussion, this is model (2), with NamespacePrivate=Yes, PackageQualifier=No. |
This seems to be a good fit for the direction all the discussion has leaned (including @jonmeow I think), and I'm very happy with it. With two of the leads and no real strong concerns, I'll mark this as decided. If I've misunderstood your position @jonmeow and you have concerns here, feel free to re-open. |
Filed #2001 to track the need for a proposal. |
Make the preamble of simple programs more ergonomic, by removing the `package Main` from the main package and removing the `package` declaration entirely from the main source file. Imports within a single package no longer need to, and are not permitted to, specify the package name. Partially covers #2001 / #1136. Covers #1869. Supersedes #2265. Addresses design idea #2323. --------- Co-authored-by: Jon Ross-Perkins <[email protected]> Co-authored-by: josh11b <[email protected]>
Originally raised in a Discord discussion.
Our status quo rule is that the top-level scope in a source file is the package scope -- that is the scope in which new unqualified declarations introduce names -- but that only names declared in the same source file are found by unqualified lookups in that scope. This leads to some surprising behavior where a (re)declaration in that scope can make information from a different library or source file visible:
And:
It's been suggested that we should move away from this rule, and towards a model where unqualified declarations introduce names into the same scope that unqualified lookup looks into. Three models have been proposed:
The file is in its own file scope, and the package is a scope within that, just like a namespace. Exported declarations are written as
fn MyPackage.Foo()
, and anything written at file scope without package name qualification is file local. This has the advantage of giving a default that's never silently the wrong thing (you don't accidentally export things / declare an external symbol), but the disadvantage of making the interface of a package harder to read by repeating the package name a lot.The file scope is the package scope. Imports consistently make new names appear in their declared scopes. So after an import of the same package, you may have new top-level names, because the top level is the package scope.
The file scope is library scope. An
impl
file starts with the names from itsapi
file visible. Imports don't change the set of names visible because you don't import your own library. The package scope is a scope within the library scope, just like a namespace. An exported declaration in anapi
file also injects a corresponding name into the package scope (as if by a namespace declaration for a namespace, and as if by an alias for anything else). This means that a type can only be declared in a single library within a package, never forward-declared in one library and defined in another, and that an overload set can't be split across libraries.From discussion on Discord, we didn't like (1) due to the significant ceremony of mentioning your own package name whenever declaring any part of an exported entity. (3) is more complex than (2), and has more ceremony than (1) when mentioning parts of other libraries in your package; however, it gives us a library scope that permits a library to define its own private names that are shared within the library, and it gives us the property that every unqualified name finds a library-local result (unlike (1) and the status quo which give a file-local result).
The text was updated successfully, but these errors were encountered: