Open BryantLam opened 5 years ago
One concern I have is that if qualified access from use statements is not removed in #13978, re-exports could become confusing to learn because all public use locations would also have the qualified access re-exported as if a public import occurred too. For Case 4, it could be confusing that M.fn() would also work.
Would limiting re-export of qualified naming to only when an explicit import is present be less confusing? E.g. for a set of modules defined like this
module A {
var x: int;
}
module B {
use A;
}
module C {
import A;
}
would it be more or less confusing if C.x
worked but B.x
didn't?
I view the enabling of qualified access through use
statements as a secondary concern, especially once import
statements are implemented. While I like having it part of use
statements for convenience, I wouldn't object to limiting it to only enabling qualified access in the scope in which it is defined, if that made it more palatable.
I think I would be okay with a reexport
symbol in principle - I prefer having an explicit indicator that a symbol will be accessible via a different qualified naming path rather than having it be an additional property of something that is more focused on the current scope, though I am a little leery of adding so many keywords.
I wouldn't object to limiting it to only enabling qualified access in the scope in which it is defined
So the example would be this, right?
module A {
var x: int;
// A.x available because of current-module-name rule
}
module B {
use A;
// A.x potentially available here because of new "used symbol in scope defined" rule
}
module C {
import A;
// A.x is always available here
}
module BB {
use B;
// A.x is not available here
// B.x is available here
}
module CC {
import C;
// C.A.x is available here
}
If that's the case, I'd consider adopting it. We could describe it as "A use statement always creates a private symbol referring to the module symbol itself to enable qualified access. The module symbol is not exported to other modules even if the use statement is public. In contrast, a public import statement will create a public symbol referring to the imported module, so that if module M
contains (public) import A
then a user/importer of M could access A
itself.
Somehow I didn't follow this part:
Would limiting re-export of qualified naming to only when an explicit import is present be less confusing would it be more or less confusing if C.x worked but B.x didn't?
Is there something wrong with the example? I might be just getting confused because it doesn't say where we are considering C.x or B.x working. I'm hoping that my variant on the example above seems like an obvious restatement of what you were saying...
Yeah, that's the right interpretation, thanks!
Continuing this comment - https://github.com/chapel-lang/chapel/issues/14407#issuecomment-582548651 - if a method is private (which we don't have yet, see #6067) - then I don't think it should be possible to use a re-export to make that method public again. The reason is that the method exists in a nested namespace (say the class on which it is defined) and since use
/ import
will control namespaces/what is in scope, but not what methods are available, it won't make sense to allow re-export of the methods that case.
Of course re-export could be used to control visibility of the type with the methods.
I think this is O.K. because I think the purpose of re-exporting is to control namespace creep rather than to enforce public/private.
Re-exporting is when a module makes available symbols defined in another module as if they were defined in that module.
Design discussion. Subthread under #13831 and #13978.
Motivation: Re-exports are used to expose a public interface that is different than the internal module hierarchy.
What is the behavior expected from re-exports? Is it sufficient to attach this behavior to
public import
andpublic use
?While subject to change, these candidate behaviors seem reasonable to me because
use
andimport
statements would have their own intended use cases.One concern I have is that if qualified access from
use
statements is not removed in #13978, re-exports could become confusing to learn because allpublic use
locations would also have the qualified access re-exported as if apublic import
occurred too. For Case 4, it could be confusing thatM.fn()
would also work.Though, one alternative path is to check if
private import
is used and prevent the qualified access: https://github.com/chapel-lang/chapel/issues/13528#issuecomment-526140095. This approach would work, but it also feels like an unfortunate consequence thatuse
statements also have qualified access because now the visibility of the module symbol is tied to a special case / precedence order ofprivate import
andpublic use
.Another approach is to consider the suggestions from Revisiting modules, take 3: make
import
statements only do imports and have some newreexport
keyword to make desired symbols visible to other modules. The same could be applied touse
statements.Privacy specifiers on
import
/use
statements are no longer necessary and it would be a clean separation of functionality because re-exporting wouldn't be tied touse
orimport
statements anymore, ... but it also feels unnecessary ifpublic import
andpublic use
are sufficient, especially with "learnability" precedence in Rust.