[swift-evolution] [Proposal][Discussion] Qualified Imports

Pyry Jahkola pyry.jahkola at iki.fi
Thu Jul 21 05:19:27 CDT 2016

I think we're getting somewhere.

> On 21 Jul 2016, at 12:01, Robert Widmann <rwidmann at apple.com> wrote:
>>> (An excerpt about the hiding directive <https://github.com/CodaFi/swift-evolution/blob/ab091043daa62158bd3337a2a2a467be3e16ff18/proposals/XXXX-qualified-imports.md#detailed-design>)
>> 4. Hmm, the above design detail is easily missed when reading the proposal. So what you're proposing is that the hiding of Date essentially turns Date into a type that the code will know almost nothing about, except it's something we can pass to other types' methods that expect a Date, am I right?
>> What's the benefit in that? Do you anticipate it will make importing lighter somehow, possibly improving compilation or library loading speed? You mentioned something along those lines in the Motivation, but I'm not sure if I got it right here.
> It is so that use of an API in a framework like Foundation does not necessitate polluting your Swift files with a million using imports - thus defeating the purpose of this proposal.

You explained further below what else `hiding` imports can be used for. But my comment was motivated by your example of `import Foundation hiding (Date)`. 

I fail to see how the lack of `hiding` imports would imply Swift files with a lot using imports. In my counting, they amount to at most one extra using import. (And I propose ~10 lines below what would get that to zero.)

The simple reason I could see someone write `import Foundation hiding (Date)` is because another imported module "Julian" she uses happens to export another Date type which she wants to use instead. And if we could just as well solve that use case by making it so that

    import Foundation
    import Julian, Julian using (Date)
    assert(Date.self == Julian.Date.self)

brings in both modules but makes Julian.Date the one that Date is shorthand of.

Besides, Joe and friends offered the use of `*` to mean "and everything else", which is problematic because `*` also happens to be an operator function name. But we could make it so that the underscore imports everything else (without explicit qualification), making the above example exactly equivalent to:

    import Foundation using (_)   // same as `import Foundation`
    import Julian using (_, Date) // bring in Date explicitly and all else implicitly
    assert(Date.self == Julian.Date.self)

> DateFormatter alone needs to know about Date, String, TimeInterval, Calendar, Locale, and TimeZone.  Each of those needs to know about additional components.  To require an import decl to use these types explicitly would be madness.  You'd just wind up importing Foundation anyway.

"You'd just wind up importing Foundation anyway" is an argument that doesn't seem imply from the given Motivation section AFAICT.

I can see three kinds of problems here (objective or subjective), caused by importing the whole Foundation:
Identifier name conflicts, which we could solve with just the `import Module using (name, Name, _)` syntax, essentially indicating which Module should be preferred.

Identifier names shadowing module names, where e.g. a `private enum Foundation {}` disables the fully qualified access to the Foundation module's API). This remains an unsolved problem, but the introduction of qualified module imports `import Foundation as Foundation` or `import Foundation as F` (or, heh, `import Foundation as is`) would be one way of going about it.

Auto-completer "hygiene", or being explicit which part of a large imported API is not considered appropriate to use by the author.
Point 3 is the only problem that `hiding` imports could be uniquely used for, especially if they can be narrowed down to members of imported types and extensions (e.g. Swift.String.UTF8View, as given in the proposal). But is that worth having?

>> 5. These chaining rules do fit on the back of a napkin, but I'm not sure if we need them at all. I'm not convinced we need `hiding` imports, and without hiding imports, we need no rules for the order of imports.
> How else are we to allow you to import a using type but not any of its member types?

I don't know. I'm asking "why?" not "how?"

> How could we support removing APIs related to NSCell in AppKit apps? How about NSStream everywhere else? How else can we allow, in the future, the ability to import NSObject but none of its KVO-related members?

I'm saying you should explain this in the Motivation. Or consider moving the `hiding` import syntax into a further proposal.

> A hiding import is an invariant: It says a particular API should never be considered for use in this file.  The very act of 'using' an identifier means you are hiding all others.  The very act of 'hiding' an identifier means you are using all others.

That is very logical. But it would be better if there was a real-world example use case given where the use of `hiding` was a clear win over a combination of `import` and `import ... using (...)`. The current one about Date isn't very convincing because instead of hiding one, it could be solved by highlighting (using) the other.

>> What I will keep suggesting is that `using` imports actually take up the name in the file-local scope such that nothing else in the same file's scope — be it another `import ... using (...)`, a local type declaration, function, or value — can declare the same name with a different meaning. That way, a plain
>>     import Foo
>> can import everything from Foo, while another
>>     import Foo using (Bar)
>> can be used to explicitly choose the Bar the code is about to use.
> That was the plan.  You will receive an "invalid redeclaration" error as always. 


— Pyry

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160721/4142efc9/attachment.html>

More information about the swift-evolution mailing list