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

Robert Widmann rwidmann at apple.com
Thu Jul 21 12:02:35 CDT 2016


> On Jul 21, 2016, at 9:41 AM, Joe Groff <jgroff at apple.com> wrote:
> 
> 
>> On Jul 20, 2016, at 1:59 PM, Xiaodi Wu via swift-evolution <swift-evolution at swift.org> wrote:
>> 
>> Why is hiding in-scope but renaming out-of-scope? Both are additive to Swift, and as has been argued by others, the former is a special case of the latter.
> 
> Hiding also doesn't seem useful to me at all. The main use case I can see is to resolve a name conflict introduced between two import-everything declarations, or between an imported and local name, and both of these use cases seem better served to me by a name lookup rule that locals are favored over qualified imports, which in turn are favored over everything imports. 'hiding' puts the import declaration in the wrong place. Consider that:
> 
> 	import Foo hiding foo
> 	import Bar
> 
> 	foo()
> 
> declares the un-import of 'foo' next to 'Foo'. The user (or IDE) has to do the mental gymnastics to figure out that 'foo()' refers to Bar.foo() by omission. This is much clearer expressed with 'using', which puts the disambiguation next to the chosen module:
> 

We were already doing those gymnastics before (in fact, one of the diagnostics for the old style would remind you of the correct decl kind if you thought a struct was a class etc.  Clearly, we already know).  If you want to be explicit, 

import Bar using (foo)
import Foo hiding (foo)

import Bar

Works just fine.  

The lookup rule seems to be justifying introducing the “confusing” example above.  It would definitely work, but why not just be explicit about what you’re using and what you’re hiding?

> 	import Foo
> 	import Bar
> 	import Bar using foo // favor Bar.foo over Foo.foo
> 
> 	foo()
> 
> 'using' is also more resilient against module evolution; as modules gain new members, their clients would potentially be forced to play whack-a-mole with 'hiding' as new conflicts are introduced. A user who diligently uses qualified imports doesn't need to worry about that. I would suggest removing 'hiding' in favor of a rule like this.

Is this not the same case with using declarations as the client module evolves?  It is more likely that a using import will grow in size as a module evolves than a hiding one - after all a hiding import says you explicitly intend to use everything else.  And if you start hiding enough identifiers that it becomes unwieldy you can always flip the script and use a using directive.  The same applies the other way around for big using clauses.

> 
> -Joe
> 
>> On Wed, Jul 20, 2016 at 15:55 Brandon Knope <bknope at me.com> wrote:
>> I meant is there any reason for requiring parentheses 
>> 
>> On Jul 20, 2016, at 4:53 PM, Robert Widmann <rwidmann at apple.com> wrote:
>> 
>>> Renaming is out of scope for this proposal, that’s why.
>>> 
>>>> On Jul 20, 2016, at 1:26 PM, Brandon Knope <bknope at me.com> wrote:
>>>> 
>>>> I prefer this 100x more
>>>> 
>>>> Is there any reason why this wouldn't work?
>>>> 
>>>> Brandon 
>>>> 
>>>> On Jul 20, 2016, at 4:13 PM, Xiaodi Wu <xiaodi.wu at gmail.com> wrote:
>>>> 
>>>>> Yeah, I'd be happy to lose the parentheses as well.
>>>>> 
>>>>> In the last thread, my take on simplifying the proposed syntax was:
>>>>> 
>>>>> ```
>>>>> import Swift using String, Int
>>>>> 
>>>>> // or, for hiding:
>>>>> import Swift using Int as _
>>>>> ```
>>>>> 
>>>>> The key simplification here is that hiding doesn't need its own contextual keyboard, especially if we support renaming (a huge plus in my book), as renaming to anything unused (or explicitly to `_`) is what hiding is all about.
>>>>> On Wed, Jul 20, 2016 at 15:01 Brandon Knope <bknope at me.com> wrote:
>>>>> 
>>>>> 
>>>>> On Jul 20, 2016, at 3:08 PM, Xiaodi Wu via swift-evolution <swift-evolution at swift.org> wrote:
>>>>> 
>>>>>> As Joe and others mentioned in the previous thread, this syntax could be greatly simplified in ways that resemble analogous facilities in other languages. In particular I think it's alarmingly asymmetrical that, in your proposal, `import Swift using (String)` imports *only* String while `import Swift hiding (String)` imports *everything but* String. This becomes evident when chained together:
>>>>>> 
>>>>>> ```
>>>>>> import Swift using (String, Int)
>>>>>> // imports only String and Int
>>>>>> import Swift using (String, Int) hiding (String)
>>>>>> // imports only Int
>>>>>> import Swift hiding (String, Int)
>>>>>> // imports everything except String and Int
>>>>>> import Swift hiding (String, Int) using (String)
>>>>>> // imports *nothing*? nothing except String? everything except Int? confusing.
>>>>>> ```
>>>>>> 
>>>>>> By contrast, Joe's proposed syntax (with some riffs) produces something much more terse *and* much more clear:
>>>>>> 
>>>>>> ```
>>>>>> import Swift.*
>>>>>> import Swift.(Int as MyInt, *)
>>>>>> import Swift.(Int as _, *)
>>>>>> ```
>>>>> 
>>>>> I really don't find this much clearer than the proposed one. The proposal reads much clearer. 
>>>>> 
>>>>> Joe's syntax has a lot going on in my opinion.
>>>>> 
>>>>> For the proposal, do we really need the parentheses? It makes the syntax look heavier
>>>>> 
>>>>> Brandon 
>>>>> 
>>>>> 
>>>>> 
>>>>>> 
>>>>>> 
>>>>>> On Wed, Jul 20, 2016 at 1:52 PM, Robert Widmann via swift-evolution <swift-evolution at swift.org> wrote:
>>>>>> Hello all,
>>>>>> 
>>>>>> I’d like to thank the members of the community that have guided the revisions of this proposal.  We have decided to heed the advice of the community and break down our original proposal on modules and qualified imports into source-breaking (qualified imports) and additive (modules) proposals.  As qualified imports is the change most suited to Swift 3, we are pushing that proposal now as our final draft.
>>>>>> 
>>>>>> It can be had inline with this email, on Github, or as a gist.
>>>>>> 
>>>>>> Thanks,
>>>>>> 
>>>>>> ~Robert Widmann
>>>>>> 
>>>>>> Qualified Imports Revisited
>>>>>> 
>>>>>> 	• Proposal: SE-NNNN
>>>>>> 	• Authors: Robert Widmann, TJ Usiyan
>>>>>> 	• Status: Awaiting review
>>>>>> 	• Review manager: TBD
>>>>>> 
>>>>>> Introduction
>>>>>> 
>>>>>> We propose a complete overhaul of the qualified imports syntax and semantics.
>>>>>> 
>>>>>> 
>>>>>> 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. In addition, though this import specifies exactly one class be imported from Foundation, the actual semantics mean Swift will recursively open all of Foundation's submodules so you can see, and use, every other identifier anyway - and they are not filtered from code completion. Qualified imports deserve to be first-class in Swift, and that is what we intend to make them with this proposal.
>>>>>> 
>>>>>> 
>>>>>> 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.
>>>>>> 
>>>>>> 
>>>>>> Detailed design
>>>>>> 
>>>>>> Qualified import syntax will be revised to the following
>>>>>> 
>>>>>> import-decl -> import <import-path> <(opt) import-directive-list>
>>>>>> import-path -> <identifier>
>>>>>>            -> <identifier>.<identifier>
>>>>>> import-directive-list -> <import-directive>
>>>>>>                      -> <import-directive> <import-directive-list>
>>>>>> import-directive -> using (<identifier>, ...)
>>>>>>                 -> hiding (<identifier>, ...)
>>>>>> 
>>>>>> This introduces the concept of an import directive. An import directive is a file-local modification of an imported identifier. A directive can be one of 2 operations:
>>>>>> 
>>>>>> 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)
>>>>>> 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.
>>>>>> 
>>>>>> // Imports `DateFormatter` but the declaration of `Date` is hidden.
>>>>>> import Foundation
>>>>>> using (DateFormatter)
>>>>>> 
>>>>>> 
>>>>>> var d = DateFormatter().date(from: "...") // Valid
>>>>>> var dt : Date = DateFormatter().date(from: "...") // Invalid: Cannot use name of hidden type.
>>>>>> 
>>>>>> d
>>>>>> .addTimeInterval(5.0) // Invalid: Cannot use members of hidden type.
>>>>>> 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)
>>>>>> Because import directives are file-local, they will never be exported along with the module that declares them.
>>>>>> 
>>>>>> 
>>>>>> 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. 
>>>>>> 
>>>>>> 
>>>>>> Alternatives considered
>>>>>> 
>>>>>> A previous iteration of this proposal introduced an operation to allow the renaming of identifiers, especially members. The original intent was to allow file-local modifications of APIs consumers felt needed to conform to their specific coding style. On review, we felt the feature was not as significant as to warrant inclusion and was ripe for abuse in large projects.
>>>>>> 
>>>>>> 
>>>>>> _______________________________________________
>>>>>> swift-evolution mailing list
>>>>>> swift-evolution at swift.org
>>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>>>> 
>>>>>> 
>>>>>> _______________________________________________
>>>>>> swift-evolution mailing list
>>>>>> swift-evolution at swift.org
>>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>> 
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
> 

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


More information about the swift-evolution mailing list