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

Robert Widmann rwidmann at apple.com
Thu Jul 21 04:01:59 CDT 2016


~Robert Widmann

2016/07/21 1:43、Pyry Jahkola <pyry.jahkola at iki.fi> のメッセージ:

> Back to the pre-proposal itself,
> 
> I support taking it forward and it being accepted in some form. However, I remain unconvinced that we'd ever want `hiding` imports in Swift.
> 
> Here are a few specific comments on the text (8 in total):
>> Motivation
>> 
>> The existing syntax for qualified imports from modules is needlessly explicit, does not compose, and has a default semantics that dilutes the intended meaning of the very operation itself. Today, a qualified import looks something like this
>> 
>> import class Foundation.Date
>> This means that clients of Foundation that wish to see only Date must know the exact kind of declaration that identifier is.
>> 
> 
> 1. Given this motivation, isn't it sufficient being able to import without knowing the exact kind of declaration? In other words, I agree we want `using` imports, but what motivates `hiding` imports really?
> 
> As it was pointed out by Félix, the use of `hiding` imports moves away the focus of what is imported into what is not imported, and while name conflict problems usually arise when importing another module on top of already working code, I think it's better to fix them at the import statement which brings in the winning name declaration, i.e. the `using` import.
> 
> 
>> Proposed solution
>> 
>> The grammar and semantics of qualified imports will change completely with the addition of import qualifiers and import directives. We also introduce two new contextual keywords: using and hiding, to facilitate fine-grained usage of module contents.
>> 
> 
> 2. Is there a typo above? The proposal does not mention import qualifiers anywhere else. I guess those are part of what got postponed into later proposals.
> 

It is.
> 
>> Detailed design
>> 
>> 1) using: The using directive is followed by a list of identifiers for non-member nominal declarations within the imported module that should be exposed to this file. 
>> 
>> // The only visible parts of Foundation in this file are 
>> // Foundation.Date, Foundation.DateFormatter, and Foundation.DateComponents
>> //
>> // Previously, this was
>> // import class Foundation.Date
>> // import class Foundation.DateFormatter
>> // import class Foundation.DateComponents
>> import Foundation using (Date, DateFormatter, DateComponents)
> 3. I support the idea of `using` imports. That's something we do need, and its introduction is sufficient to cover the existing use cases of the old selective import syntax about to be deprecated here.
> 
> 
> 
>> 2) hiding: The hiding directive is followed by a list of identifiers for non-member nominal declarations within the imported module that should be hidden from this file.
>> 
>> // Imports all of Foundation except `Date`
>> import Foundation hiding (Date)
>> As today, all hidden identifiers do not hide the type, they merely hide that type’s members and its declaration. For example, this means values of hidden types are still allowed. Unlike the existing implementation, using their members is forbidden.
>> 
> 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.  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.

> 
> 
> 
>> Import directives chain to one another and can be used to create a fine-grained module import:
>> 
>> // This imports Swift.Int, Swift.Double, and Swift.String but hides Swift.String.UTF8View
>> import Swift using (String, Int, Double) 
>>              hiding (String.UTF8View)
>> Directive chaining occurs left-to-right:
>> 
>> // This says to 1) Use Int 2) Hide String 3) rename Double to Triple.  It is invalid
>> // because 1) Int is available 2) String is not, error.
>> import Swift using (Int) hiding (String)
>> // Valid.  This will be merged as `using (Int)`
>> import Swift using () using (Int)
>> // Valid.  This will be merged as `hiding (String, Double)`
>> import Swift hiding (String) hiding (Double) hiding ()
>> // Valid (if redundant). This will be merged as `using ()`
>> import Swift using (String) hiding (String)
> 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?  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?  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.

> 
> 
>> Because import directives are file-local, they will never be exported along with the module that declares them.
>> 
> 6. +1 on imports being file-local, that's essentially what we have today.
> 
> 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.
 
> 
> 
> 7. (Off-topic to this proposal.) Given that you briefly expressed the interest in turning imports `public` or `internal` — some of which would indeed be useful in a better module system —, I think it's curious in that design that the default visibility level of imports would likely be `fileprivate`, which is different from the rest of Swift. That said, I think it's absolutely right for imports to remain file-local by default, because explicit tends to be better than implicit.
> 
> 
>> Impact on existing code
>> 
>> Existing code that is using qualified module import syntax (import {func|class|typealias|class|struct|enum|protocol} <qualified-name>) will be deprecated and should be removed or migrated. 
>> 
> 8. +1 on deprecating the old selective import syntax.
> 
> Thank you for driving this forward!
> 
> — Pyry
> 
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160721/d762ffc3/attachment.html>


More information about the swift-evolution mailing list