[Proposal][Discussion] Qualified Imports

~Robert Widmann

2016/07/20 19:01、Xiaodi Wu via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> のメッセージ:

“The Phase Distinction” is a semantic one, not one built into the import system itself.

I understand. To rephrase my question: why introduce this semantic distinction to Swift?

What I meant is that even the system I’m modeling this on makes a distinction between import directives that actually expose identifiers to modules and import directives that modify identifiers that are already in scope.

This is, IMO, very complex. I appreciate enormously the conceptual simplicity of the current Swift approach which, for all of its deficiencies, has only one import directive that does what it says on the tin: it exposes identifiers. I'm not bothered if it gains the ability to expose identifiers differently from one file to the next without keywords firewalled from each other to preserve the notion of phases of import.

We are not changing the unqualified Swift import system. Take a gander at the proposal again, or even the first draft. Swift has a particularly strange syntax for qualified imports that hasn’t received attention since it was first introduced 2 major versions ago. That thing allows quite a variety of senseless variants that can be both completely expressed by and subsumed by `using` and `hiding`.

My sense, which I think has been echoed by others, is that the proposed solution is syntactically complex, and now that I understand that you're thinking through a multi-phase concept, also conceptually multilayered. I'm not arguing that the existing syntax for qualified imports doesn't need changing, only that there is room for radical simplification of the proposed solution IMO. As I re-read this proposal once more, it strikes me that the motivating issues identified (needlessly specific, does not compose, etc.) don't clearly argue for the specific direction proposed as opposed to alternatives like Joe's.

Perhaps they need to reread the proposal. Syntactically complex how? We're introducing two keywords and using tuple syntax. Our grammar changes are laid bare and take up 7 lines. I think there might be a general air of confusing semantics changes with syntax changes.

I can't speak for the general air, but putting on my disinterested reader hat, I can see why the confusion might arise--

The Motivation section begins:
"The existing syntax for qualified imports..."

And the Proposed Solution begins:
"The grammar and semantics of qualified imports..."

But the Detailed Design begins:
"Qualified import syntax will be revised..."

It's neither here nor there in terms of the proposal content, but suffice it to say that if one strings together the topic sentences in your proposal, the overarching narrative to be gleaned here is: "The current syntax for qualified imports is no good; therefore, we revise the semantics of qualified imports by changing the syntax." Sure.

Ne’er the twain shall meet.

Yes, you explained this concept very clearly as it applies to Agda. But I just don't see why we should care to have this distinction. Yet you are very adamant about it. What am I missing?

We should care because that is precisely what the two operations do. `using` and `hiding` introduce things or remove things from scope which is a very different operation from taking something that is already in scope and giving it a new name.

Perhaps I'm not phrasing my question very cogently. Of course, if we are to have `using`, `hiding`, and `renaming`, we must observe the distinctions between them.

If you don’t want to think of them as part of the same import process, think of them instead in terms of their Swift equivalents today.

import Foundation using (Date) == import struct Foundation.Date
import Foundation hiding (Date) == import Foundation; @unavailable(*, “…") typealias Date = Foundation.Date
import Foundation using (TimeInterval) renaming (TimeInterval, to: Time) == import typealias Foundation.TimeInterval; typealias Time = Foundation.TimeInterval

Notice how it takes two declarations to create a renaming? It is not simple to drop being explicit about which names are actually in scope and expect a renaming to just implicitly slip in a using declaration. Nor is it simple to imagine _ as some magical namespace that you can pack away unwanted definitions into. using and hiding are very physical things and the rules for their behavior should be obvious and unambiguous - the proposal contains some examples of valid and invalid declarations to help with that.

The examples worry me, in fact. That we might need to contemplate the behavior of a statement such as `import Foo using () hiding () hiding () using () hiding ()` suggests it's perhaps a little over-engineered for the purpose. Why allow chaining of `using` and `hiding` anyway? The only example given is of a nested type, which suggests nesting would be the way to go:

We allow chaining specifically to avoid that nesting behavior. Let's break up that chained import line by line to see why

import Swift using (String, Int, Double, Character)
                      hiding (String.UTF8View)

import Swift // Scope contains {all identifiers in Swift}
using (String, Int, Double, Character) // Scope contains {String.*, Int.*, Double.*, Character.*}
                      hiding (String.UTF8View) // Scope contains {{String.* - String.UTF8View.*}, Int.*, Double.*, Character.*}

We express the exact same example in a much more human-readable way. You can read this out loud in plain English if you don't believe me: "import everything in Swift that is in String, Int, Double, and Character except String.UTF8View"

I have to disagree with your reasoning here. Unless I'm mistaken, the readability we're most concerned with is that of the written text, not its spoken form.

Nesting is an almost exclusively _visual_ way of organizing text and it adds real clarity on the page. Of course, if your litmus test for readability is literally trying to read it out loud, you would conclude that nesting is inferior. However, you'd make that same conclusion about so many other choices in Swift syntax (take, for instance, `:` instead of `extends` or `->` instead of `returns`). I conclude, on the other hand, that Swift is clearly not aiming to be AppleScript-like in this respect.

I disagree. Standard Library Swift reads left to right in a very carefully chosen and consistent manner - it has been refined on this list and by the team quite a lot to get it that way. You seem to think I’m arguing verbosity is the end goal (AppleScript?): I’m not. Clarity is the goal. It is unambiguous what you mean when you say

import Swift using (String, Int, Double) hiding (String.UTF8View)

This syntax fits well with the overall direction of the language itself. For example, I use whitespace here out of habit but here’s nothing stopping you from making these look like the (meta)function invocations they really are

import Swift
  using(String, Int, Double)
  hiding(String.UTF8View)

In the comments you've used the nested notation `{{String.* - String.UTF8View.*}, Int.*, Double.*, Character.*}` to explain what it is your proposed syntax means. I really do find that, with all the punctuation in it, clearer than the chained notation. Of course, written `(String hiding UTF8View), Int, Double, Character`, it would be clearer still.

Critically, it eliminates the possibility of absurd chains of `hiding () using () hiding () using ()`, which create many more ways to represent the same subset of imports than can a nested syntax.

If you wish to express this, why should the language stop you? Or better yet: we can offer diagnostics about this case just as we can offer diagnostics for doubled imports or improperly nested imports. A big part of why this proposal exists is because the existing diagnostics around qualified imports are uninformative and unhelpful.

Now reread the chaining example in the proposal.

import Swift using (String hiding (UTF8View), Int, Double)

A qualified import is defining a procedure to import a subset of identifiers. That’s it.

Right, and I think an entirely different way of thinking about this would be much easier to learn and teach. Whether using, hiding, and renaming are to be supported now, later, or never, my mental picture of how it fits together is quite simple:

Analogy--suppose I am a pickle merchant. I import Foo-branded pickles from vendor X. I must re-label them with the right nutritional information before I can sell in this country. I can have labels printed saying that they are Foo-branded pickles. I can have them branded as Bar-branded pickles. Or I can have the labels deliberately misprinted, and then these pickles will never see the light of day. Point is, each of these is an active choice; even if I sell these as Bar-branded pickles, it's not that these pickles reached the domestic market as Foo-branded pickles, after which I scratched out the label with a Sharpie. These pickles had no domestic brand until I gave it one.

Back to importing modifiers--I import type Foo from module X. In my code, I need to make a choice to call this type Foo, or Bar, or nothing at all. In other words, there is only one directive, importing, and I am importing `Foo as Foo`, `Foo as Bar`, or `Foo as _`. Meanwhile, `import X using Foo` or `import X.Foo` (whatever the color of the bikeshed) would just be a shorthand for `import X using Foo as Foo` or `import X.Foo as Foo`. In this conceptualization, if I choose to import Foo as Bar, it's not that I'm importing Foo into the scope, then changing the identifier to Bar. The only identifier it ever has in this scope is Bar.

And I’m the one with the complex semantics? :)

I'm just trying to put into words what I'm familiar with after working in other languages such as Python.

Python may not be the right mindset for this. Their import story is much simpler because of their module system and generally simpler programming model.

How about this:

Using and Hiding relate to each other the way && and || do for bools. If && can be said to “prefer to return false, but return true given no other alternative” and || can be said to “prefer returning true, but return false given no other alternative”, then hiding can be said to “prefer importing all identifiers unless told not to in specific instances” and using can be said to “prefer importing no identifiers unless told to in specific instances”.

import Module.Name using (A, B, C, …) === import Module.Name hiding (ALL_NAMES - {A, B, C, ...})
import Module.Name hiding (A, B, C, …) === import Module.Name using (ALL_NAMES - {A, B, C, ...})

That seems a particularly simple explanation to me. Let me know if anything else is unclear.

Your mental framework is clear. It's one that's just not found in very many other languages. Many of these have import declarations (or similar) with simpler syntax, yet they seem to address at least some of the problems that motivate your proposal. I guess my question in the end is, why have you chosen Agda as the basis for qualified imports in Swift and not one of these other languages?

I chose Agda because it's the only major language I could find that treated identifiers like notation and used its module system for real organization of code; it's just a name, it can change if you want it to. The entire language is flexible. You can redefine functions with just an =, you can give new syntactic transformations without having to write crazy macros or worrying about hygiene. You get so much support from the type system too that all of these features just work together and you can sit back and feel satisfied that your tools pushed you to write good code. I wrote it with Agda in mind because they took the time to think about the interactions between modules, code, and scope in a way we just haven't had the time to yet.

Many of these imports have simpler syntax but don't chain so lose expressiveness (Java). Or may have simpler surface syntax but don't properly interact with a moduleless system (Python, Java). Or may have a simpler syntax and give way to ambiguities (Python) when used in Swift. Agda is unambiguous, scalable, extensible, and simple.

Please don't confuse what's new with what's complex. But at the same time if there are any unexplainable loopholes we should know about them. I just haven't heard much I couldn't point to the proposal about yet so I don't see a reason to change.

Thanks for this insight. It's clear that you're aiming at a far richer system in the future. However, it's hard to gauge how far Swift will eventually scale/extend what you're proposing here. I don't think there's any denying that the solution you've chosen introduces more involved semantics and a more verbose syntax than what's strictly necessary to address the specific motivating problem you give in this particular instance. Without a better sense of the outer bounds of how far this system will eventually be pushed, it's hard to judge whether the eventual payoff will be "worth it." And without any mention of the grander plan, I suspect the feedback you're going to get will continue along the path of trying to tear away whatever you're trying to put in place for the future which is not discernibly necessary for solving the immediate problem at hand.

If you believe this proposal is not extensible then cite a particular example, otherwise I can only say that I can’t tell the future. The syntax presented here is inspired by a tiny, tiny fragment of a language with an incredibly rich import mechanism and module system. It’s unclear how much of that system fits with Swift current - we already tried to formalize quite a bit in the first draft. For right now, this proposal is trying to clean up a long-neglected part of the language. If you feel that we’ve failed in that regard then tell me. Otherwise I’m fine with my syntax being pulled in a different direction in future additive proposals. I just want to fix this part of the language before the window on source-breaking changes closes for a while.

···

On Jul 20, 2016, at 10:04 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Wed, Jul 20, 2016 at 10:33 PM, Robert Widmann <devteam.codafi@gmail.com <mailto:devteam.codafi@gmail.com>> wrote:

On Wed, Jul 20, 2016 at 8:10 PM, Robert Widmann <rwidmann@apple.com <mailto:rwidmann@apple.com>> wrote:

On Jul 20, 2016, at 5:47 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:
On Wed, Jul 20, 2016 at 6:30 PM, Robert Widmann <rwidmann@apple.com <mailto:rwidmann@apple.com>> wrote:

On Jul 20, 2016, at 4:17 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:

On Wed, Jul 20, 2016 at 4:57 PM, Robert Widmann <rwidmann@apple.com <mailto:rwidmann@apple.com>> wrote:

On Jul 20, 2016, at 2:52 PM, Robert Widmann via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jul 20, 2016, at 2:35 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:

On Wed, Jul 20, 2016 at 4:24 PM, Robert Widmann <rwidmann@apple.com <mailto:rwidmann@apple.com>> wrote:

On Jul 20, 2016, at 2:19 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:

On Wed, Jul 20, 2016 at 4:06 PM, Robert Widmann <rwidmann@apple.com <mailto:rwidmann@apple.com>> wrote:

On Jul 20, 2016, at 2:04 PM, Robert Widmann via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jul 20, 2016, at 1:59 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:

Why is hiding in-scope but renaming out-of-scope?

Because hiding and renaming can be used in combination to subset out APIs, not alter them.

I mistyped. Should be "Because hiding and using can be used in combination to subset out APIs, not alter them."

Sure, I buy that.

Both are additive to Swift,

As part of this proposal, both are source-breaking.

I don't see how. If hiding were cut from the proposal, adding it later with even the exact syntax you propose should break no pre-existing code--am I wrong?

Renaming the way we originally laid it out would certainly be additive. The way you have it laid out would overlap a bit with hiding, sure, but it is still additive and (IMO, but I’m probably among a tiny minority of users that has used a proof assistant’s syntax as the basis for a proposal!) a good thing to have.

Sorry, I fear I've incorrectly communicated the point I was trying to make. I'm not advocating here for inclusion of renaming as part of this proposal. I simply think that--even though I buy your claim that hiding and using both subset out APIs--hiding has more affinity with renaming and the two facilities probably ought to be considered together, whenever that is.

Thus, I'm suggesting that it would be feasible to postpone discussion of hiding until such future time as a fully fleshed out renaming scheme is proposed. A revamped source-breaking import syntax without either hiding or renaming could be put in place now, and future addition of hiding and/or renaming would not have to be source-breaking. Is there something wrong with this argument?

There is still a useful to distinction to be made between explicitly renaming an API and explicitly hiding an API. Scala’s syntax to rename to underbar is a convenient notation for that kind of thing, but it goes against making qualified imports explicit and it means that renaming necessarily has to import identifiers into scope as well as rename them. What the OP (maybe it was you, sorry if it was) meant by “equivalent” missed the point that

import Swift hiding (String)

doesn’t translate into

import Swift renaming (String, to: _)

it translates into

import Swift hiding () renaming (String, to: _)

Renaming introducing identifiers into scope seems like a phase-shift and is not something the verb “rename” implies should happen here. It’s an interesting little hole in Agda’s module system that you can use

open A hiding (xs) renaming (ys to zs)

to mean

open A using (A; xs; ys) renaming (ys to zs)

Actually, scratch that. Their documentation explicitly mentions that hiding and renaming may not be mixed because of the phase distinction and recommend the using translation above as the way to go.

This is very illuminating. I think I've rather misunderstood what it is you're proposing. I wonder if others did also.

The syntax you proposed seemed cumbersome to me because my mental model of importing (informed by my probably superficial understanding of vanilla procedural programming languages) has only one phase: importing. This is why I proposed radically simplifying the spelling. To me, all of these operations are just sugar on a single import phase, where "stuff" from outside the module is "brought into" the module, either with the same name ("using"), a different name ("renaming"), or no name ("hiding").

But what you're saying here--if I understand correctly--is that you're proposing a multi-phase import system, where the possible phases, which can be composed in varying orders, are "using", "hiding", and "renaming". This is much, much more elaborate than I had contemplated. So beyond the bikeshedding of syntax, I'd ask: why do we need this multi-phase model of importing?

and as has been argued by others, the former is a special case of the latter.

A special case that cannot cause large-scale file-relative changes to APIs. Renaming is primarily used in other languages that treat free functions as more canonical than we do, or allow operator definitions that can be used as notation.

I don't know about 'primary use,' but the most common use I've experienced in Python, for example, is the mundane task of importing module Foo2 as Foo.

And I still want that kind of syntax. I just want to get the breaking changes out of the way to make room for it in the future.

Right. See above about my argument as to which parts of your proposal have to be source-breaking, and which don't.

In those cases, you often have your own notation you’d like to use. In Swift, such changes should be rare enough that if you can’t solve them with a disambiguating qualified import then you can just redeclare the identifier some other way (typealias, top-level let, wrapper class, whatever).

You've already stripped out renaming of members from the proposal. I agree wholeheartedly. The only flavor of renaming I'm thinking of here is equivalent to a fileprivate typealias and hiding, which cannot be done in this version of the proposal because hiding always comes before typealiasing and you can't typealias what isn't imported. It isn't about altering APIs any more than a fileprivate typealias can be thought of as altering APIs.

In the sense that you can’t use the original identifier if you rename it, it is an alteration. John brought up a great point about exporting these things and how it could be a potentially dangerous thing. Even used locally, there’s the potential for people to specify 500 lines of import renaming crap that has to be copypasta’d throughout the codebase to maintain that particular style - not a use-case I’ve ever seen, but the potential is there.

This is, I think, a spurious argument. I can equally have 500 lines of private typealiased crap that has to be copypasta'd.

On Wed, Jul 20, 2016 at 15:55 Brandon Knope <bknope@me.com <mailto:bknope@me.com>> wrote:
I meant is there any reason for requiring parentheses

On Jul 20, 2016, at 4:53 PM, Robert Widmann <rwidmann@apple.com <mailto:rwidmann@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@me.com <mailto:bknope@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@gmail.com <mailto:xiaodi.wu@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@me.com <mailto:bknope@me.com>> wrote:

On Jul 20, 2016, at 3:08 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@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@swift.org <mailto:swift-evolution@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 <https://github.com/apple/swift-evolution/pull/440&gt;, or as a gist <https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6&gt;\.

Thanks,

~Robert Widmann

Qualified Imports Revisited

Proposal: SE-NNNN <https://gist.github.com/CodaFi/NNNN-first-class-qualified-imports.md&gt;
Authors: Robert Widmann <https://github.com/codafi&gt;, TJ Usiyan <https://github.com/griotspeak&gt;
Status: Awaiting review
Review manager: TBD

<qualified-imports.md · GitHub

We propose a complete overhaul of the qualified imports syntax and semantics.

<qualified-imports.md · GitHub

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.

<qualified-imports.md · GitHub 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.

<qualified-imports.md · GitHub 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.

<qualified-imports.md · GitHub 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.

<qualified-imports.md · GitHub 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@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

The problem is that by specifying "import Foo using (Baz)", I get nothing else from Foo. If I only want to exclude one conflicting name, I would have:

import Foo
import Bar hiding (Baz)

In case of a conflict, my "internal monologue" is more like "take Baz from Foo" than "don't take Baz from Bar".

How else would you resolve an ambiguity than by selecting the appropriate declaration and hiding the others? Swift’s semantic analysis cannot read your mind, and neither can your (or mine) proposal for renaming syntax - in that you still have to import both modules either way. You may as well be explicit about which name you’re actually using and which ones you’re actually hiding, eh?

···

On Jul 20, 2016, at 9:37 PM, Félix Cloutier via swift-evolution <swift-evolution@swift.org> wrote:

Félix

Le 20 juil. 2016 à 20:46:18, Robert Widmann <devteam.codafi@gmail.com <mailto:devteam.codafi@gmail.com>> a écrit :

~Robert Widmann

2016/07/20 20:07、Félix Cloutier via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> のメッセージ:

My understanding is that we want "using" and "hiding" because we want the ability to either take just a few things OR leave out just a few things. With a unified "import Foo (A = B, C = D, E = _) syntax, we only get the ability to take a few things AND hide a few things.

Again, renaming is not something I want done with the same syntax as introducing and removing things from scope because they are distinct operations. This import tells me nothing from a semantic perspective and just seems easy to type rather than understand. Nowhere in the language can you find something that resembles this either.

I've never really been into a case where I badly had to *not* import a name, so while I see why it makes sense from a mathematical perspective to have "hiding", I'm not sure how much I'd miss it if it wasn't there.

I realize that it solves the ambiguous type problem <Issues · apple/swift-issues · GitHub, but I consider that it's a flawed solution. Instead of specifying from which module you want an import, you have to specify on which modules you don't want it.

You still specify which module you want to import from, so I don't see your point here. Given that Foo and Bar both define a class Baz, here's your import

import Foo using (Baz)
import Bar hiding (Baz)

What's the problem here? Isn't this exactly what you wanted to say in English (or whatever internal monologue you might have) spelled out in code? It scales immediately to multiple ambiguities and we can provide diagnostics to insert or remove identifiers in these lists to help the user out when they get stuck with an insufficiently inclusive or exclusive import list. The Python example is much more difficult to reason about from my perspective and from the perspective of the compiler. In fact, it's almost the code that's needed today to work around this problem - we're trying to fix the need for this here.

To see if we can get inspiration, I'd like to pitch an imperfect Python-like approach, where you could import a module as a namespace (and then you'd always have to write Module.Class, with modules systematically shadowing classes in the global namespace), or in addition to that, import every top-level name in the module into the file's global namespace. Names defined in multiple modules remain ambiguous unless explicitly shadowed:

private typealias OrderedSet = BTree.OrderedSet
private var foo: (Int) -> Int = Bar.foo

You would not be allowed to shadow a module with a class.

This, however, still does not solve the extension problem. Additionally, given that the default visibility for top-level names is internal, careless users could easily pollute the project's global namespace. Finally, for micro-frameworks that have a class with the same name as a module, you'd always have to write Name.Name, since the class can't shadow the module.

Félix

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Regards
(From mobile)

As cannot (and should not) hide substructures and can be added later if you so desire.

Hiding is not necessary if you import into a pseudo container... It means the ide does not have to keep track of whats here whats not on a per source file basis....

Import CoreGraphics as cg
cg.xxxxx

Collisions are always avoided and there is only adding imports. Simple.

and what's more:

  Import CoreGraphics as cg
  cg.xxxxx()

and
  Import CoreGraphics
  xxxxx()

don't even require two separate internal implementations in the compiler... Done well, the exact same code can handle both scenarios.

···

On Jul 21, 2016, at 12:38 AM, Robert Widmann <rwidmann@apple.com> wrote:

On Jul 20, 2016, at 3:36 PM, L. Mihalkovic <laurent.mihalkovic@gmail.com> wrote:

Regards
(From mobile)

On Jul 20, 2016, at 11:04 PM, Robert Widmann via swift-evolution <swift-evolution@swift.org> wrote:

On Jul 20, 2016, at 1:59 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Why is hiding in-scope but renaming out-of-scope?

Because hiding and renaming can be used in combination to subset out APIs, not alter them.

Both are additive to Swift,

As part of this proposal, both are source-breaking.

and as has been argued by others, the former is a special case of the latter.

A special case that cannot cause large-scale file-relative changes to APIs. Renaming is primarily used in other languages that treat free functions as more canonical than we do, or allow operator definitions that can be used as notation. In those cases, you often have your own notation you’d like to use. In Swift, such changes should be rare enough that if you can’t solve them with a disambiguating qualified import then you can just redeclare the identifier some other way (typealias, top-level let, wrapper class, whatever).

On Wed, Jul 20, 2016 at 15:55 Brandon Knope <bknope@me.com> wrote:

I meant is there any reason for requiring parentheses

On Jul 20, 2016, at 4:53 PM, Robert Widmann <rwidmann@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@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@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@me.com> wrote:

On Jul 20, 2016, at 3:08 PM, Xiaodi Wu via swift-evolution <swift-evolution@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@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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

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.

···

On Jul 21, 2016, at 9:41 AM, Joe Groff <jgroff@apple.com> wrote:

On Jul 20, 2016, at 1:59 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:

-Joe

On Wed, Jul 20, 2016 at 15:55 Brandon Knope <bknope@me.com> wrote:
I meant is there any reason for requiring parentheses

On Jul 20, 2016, at 4:53 PM, Robert Widmann <rwidmann@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@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@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@me.com> wrote:

On Jul 20, 2016, at 3:08 PM, Xiaodi Wu via swift-evolution <swift-evolution@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@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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I agree with your line of reasoning regarding not needing 'hiding'
(leveraging order of lookup as you noted).

···

On Thu, Jul 21, 2016 at 12:41 PM Joe Groff via swift-evolution < swift-evolution@swift.org> wrote:

> On Jul 20, 2016, at 1:59 PM, Xiaodi Wu via swift-evolution < > swift-evolution@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:

        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.

-Joe

> On Wed, Jul 20, 2016 at 15:55 Brandon Knope <bknope@me.com> wrote:
> I meant is there any reason for requiring parentheses
>
> On Jul 20, 2016, at 4:53 PM, Robert Widmann <rwidmann@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@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@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@me.com> wrote:
>>>>
>>>>
>>>> On Jul 20, 2016, at 3:08 PM, Xiaodi Wu via swift-evolution < > swift-evolution@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@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@swift.org
>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>>>
>>>>>
>>>>> _______________________________________________
>>>>> swift-evolution mailing list
>>>>> swift-evolution@swift.org
>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I am confused as to why you think this would necessitate multiple phases of import logic to implement. We already have code that essentially does this baked into the code completion system. <https://github.com/apple/swift/blob/f72bd5453f2fa4f89d075d90210cb41c124c9e74/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp#L1128-L1131&gt;

Moreover, I’m not convinced that shuffling identifiers off into a “pseudo-container” is the right idea for an operation we already know how to perform.

···

On Jul 20, 2016, at 4:05 PM, L. Mihalkovic <laurent.mihalkovic@gmail.com> wrote:

Regards
(From mobile)

On Jul 21, 2016, at 12:38 AM, Robert Widmann <rwidmann@apple.com <mailto:rwidmann@apple.com>> wrote:

As cannot (and should not) hide substructures and can be added later if you so desire.

On Jul 20, 2016, at 3:36 PM, L. Mihalkovic <laurent.mihalkovic@gmail.com <mailto:laurent.mihalkovic@gmail.com>> wrote:

Hiding is not necessary if you import into a pseudo container... It means the ide does not have to keep track of whats here whats not on a per source file basis....

Import CoreGraphics as cg
cg.xxxxx

Collisions are always avoided and there is only adding imports. Simple.

and what's more:

  Import CoreGraphics as cg
  cg.xxxxx()

and
  Import CoreGraphics
  xxxxx()

don't even require two separate internal implementations in the compiler... Done well, the exact same code can handle both scenarios.

Regards
(From mobile)

On Jul 20, 2016, at 11:04 PM, Robert Widmann via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jul 20, 2016, at 1:59 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:

Why is hiding in-scope but renaming out-of-scope?

Because hiding and renaming can be used in combination to subset out APIs, not alter them.

Both are additive to Swift,

As part of this proposal, both are source-breaking.

and as has been argued by others, the former is a special case of the latter.

A special case that cannot cause large-scale file-relative changes to APIs. Renaming is primarily used in other languages that treat free functions as more canonical than we do, or allow operator definitions that can be used as notation. In those cases, you often have your own notation you’d like to use. In Swift, such changes should be rare enough that if you can’t solve them with a disambiguating qualified import then you can just redeclare the identifier some other way (typealias, top-level let, wrapper class, whatever).

On Wed, Jul 20, 2016 at 15:55 Brandon Knope <bknope@me.com <mailto:bknope@me.com>> wrote:
I meant is there any reason for requiring parentheses

On Jul 20, 2016, at 4:53 PM, Robert Widmann <rwidmann@apple.com <mailto:rwidmann@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@me.com <mailto:bknope@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@gmail.com <mailto:xiaodi.wu@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@me.com <mailto:bknope@me.com>> wrote:

On Jul 20, 2016, at 3:08 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@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@swift.org <mailto:swift-evolution@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 <https://github.com/apple/swift-evolution/pull/440&gt;, or as a gist <https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6&gt;\.

Thanks,

~Robert Widmann

Qualified Imports Revisited

Proposal: SE-NNNN <https://gist.github.com/CodaFi/NNNN-first-class-qualified-imports.md&gt;
Authors: Robert Widmann <https://github.com/codafi&gt;, TJ Usiyan <https://github.com/griotspeak&gt;
Status: Awaiting review
Review manager: TBD

<qualified-imports.md · GitHub

We propose a complete overhaul of the qualified imports syntax and semantics.

<qualified-imports.md · GitHub

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.

<qualified-imports.md · GitHub 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.

<qualified-imports.md · GitHub 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.

<qualified-imports.md · GitHub 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.

<qualified-imports.md · GitHub 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@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

~Robert Widmann

のメッセージ:

“The Phase Distinction” is a semantic one, not one built into the
import system itself.

I understand. To rephrase my question: why introduce this semantic
distinction to Swift?

What I meant is that even the system I’m modeling this on makes a

distinction between import directives that actually expose identifiers to
modules and import directives that modify identifiers that are already in
scope.

This is, IMO, very complex. I appreciate enormously the conceptual
simplicity of the current Swift approach which, for all of its
deficiencies, has only one import directive that does what it says on the
tin: it exposes identifiers. I'm not bothered if it gains the ability to
expose identifiers differently from one file to the next without keywords
firewalled from each other to preserve the notion of phases of import.

We are *not* changing the unqualified Swift import system. Take a
gander at the proposal again, or even the first draft. Swift has a
particularly strange syntax for qualified imports that hasn’t received
attention since it was first introduced 2 major versions ago. That thing
allows quite a variety of senseless variants that can be both completely
expressed by and subsumed by `using` and `hiding`.

My sense, which I think has been echoed by others, is that the proposed
solution is syntactically complex, and now that I understand that you're
thinking through a multi-phase concept, also conceptually multilayered. I'm
not arguing that the existing syntax for qualified imports doesn't need
changing, only that there is room for radical simplification of the
proposed solution IMO. As I re-read this proposal once more, it strikes me
that the motivating issues identified (needlessly specific, does not
compose, etc.) don't clearly argue for the specific direction proposed as
opposed to alternatives like Joe's.

Perhaps they need to reread the proposal. Syntactically complex how?
We're introducing two keywords and using tuple syntax. Our grammar changes
are laid bare and take up 7 lines. I think there might be a general air of
confusing semantics changes with syntax changes.

I can't speak for the general air, but putting on my disinterested reader
hat, I can see why the confusion might arise--

The Motivation section begins:
"The existing syntax for qualified imports..."

And the Proposed Solution begins:
"The grammar and semantics of qualified imports..."

But the Detailed Design begins:
"Qualified import syntax will be revised..."

It's neither here nor there in terms of the proposal content, but suffice
it to say that if one strings together the topic sentences in your
proposal, the overarching narrative to be gleaned here is: "The current
syntax for qualified imports is no good; therefore, we revise the semantics
of qualified imports by changing the syntax." Sure.

Ne’er the twain shall meet.

Yes, you explained this concept very clearly as it applies to Agda. But
I just don't see why we should care to have this distinction. Yet you are
very adamant about it. What am I missing?

We should care because that is precisely what the two operations *do*.
`using` and `hiding` introduce things or remove things from scope which is
a very different operation from taking something that is already in scope
and giving it a new name.

Perhaps I'm not phrasing my question very cogently. Of course, if we are
to have `using`, `hiding`, and `renaming`, we must observe the distinctions
between them.

If you don’t want to think of them as part of the same import process,

think of them instead in terms of their Swift equivalents today.

import Foundation using (Date) == import struct Foundation.Date
import Foundation hiding (Date) == import Foundation; @unavailable(*,
“…") typealias Date = Foundation.Date
import Foundation using (TimeInterval) renaming (TimeInterval, to: Time)
== import typealias Foundation.TimeInterval; typealias Time =
Foundation.TimeInterval

Notice how it takes two declarations to create a renaming? It is not
simple to drop being explicit about which names are actually in scope and
expect a renaming to just implicitly slip in a using declaration. Nor is
it simple to imagine _ as some magical namespace that you can pack away
unwanted definitions into. using and hiding are very physical things and
the rules for their behavior should be obvious and unambiguous - the
proposal contains some examples of valid and invalid declarations to help
with that.

The examples worry me, in fact. That we might need to contemplate the
behavior of a statement such as `import Foo using () hiding () hiding ()
using () hiding ()` suggests it's perhaps a little over-engineered for the
purpose. Why allow chaining of `using` and `hiding` anyway? The only
example given is of a nested type, which suggests nesting would be the way
to go:

We allow chaining specifically to avoid that nesting behavior. Let's
break up that chained import line by line to see why

import Swift using (String, Int, Double, Character)
                      hiding (String.UTF8View)

import Swift // Scope contains {all identifiers in Swift}
using (String, Int, Double, Character) // Scope contains {String.*,
Int.*, Double.*, Character.*}
                      hiding (String.UTF8View) // Scope contains
{{String.* - String.UTF8View.*}, Int.*, Double.*, Character.*}

We express the exact same example in a much more human-readable way. You
can read this out loud in plain English if you don't believe me: "import
everything in Swift that is in String, Int, Double, and Character except
String.UTF8View"

I have to disagree with your reasoning here. Unless I'm mistaken, the
readability we're most concerned with is that of the written text, not its
spoken form.

Nesting is an almost exclusively _visual_ way of organizing text and it
adds real clarity on the page. Of course, if your litmus test for
readability is literally trying to read it out loud, you would conclude
that nesting is inferior. However, you'd make that same conclusion about so
many other choices in Swift syntax (take, for instance, `:` instead of
`extends` or `->` instead of `returns`). I conclude, on the other hand,
that Swift is clearly not aiming to be AppleScript-like in this respect.

I disagree. Standard Library Swift reads left to right in a very
carefully chosen and consistent manner - it has been refined on this list
and by the team quite a lot to get it that way. You seem to think I’m
arguing verbosity is the end goal (AppleScript?): I’m not. Clarity is the
goal. It is unambiguous what you mean when you say

import Swift using (String, Int, Double) hiding (String.UTF8View)

Is it ambiguous to say `import Swift using (String hiding (UTF8View), Int,
Double)`?

This syntax fits well with the overall direction of the language itself.
For example, I use whitespace here out of habit but here’s nothing stopping
you from making these look like the (meta)function invocations they really
are

import Swift
  using(String, Int, Double)
  hiding(String.UTF8View)

In the comments you've used the nested notation `{{String.* -
String.UTF8View.*}, Int.*, Double.*, Character.*}` to explain what it is
your proposed syntax means. I really do find that, with all the punctuation
in it, clearer than the chained notation. Of course, written `(String
hiding UTF8View), Int, Double, Character`, it would be clearer still.

Critically, it eliminates the possibility of absurd chains of `hiding ()
using () hiding () using ()`, which create many more ways to represent the
same subset of imports than can a nested syntax.

If you wish to express this, why should the language stop you?

Given two options for syntax, one where expressing this chain is never
necessary or even possible, and another where it is possible and perhaps
even necessary, the former wins in terms of clarity, no?

···

On Thu, Jul 21, 2016 at 1:40 AM, Robert Widmann <rwidmann@apple.com> wrote:

On Jul 20, 2016, at 10:04 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Wed, Jul 20, 2016 at 10:33 PM, Robert Widmann <devteam.codafi@gmail.com > > wrote:

2016/07/20 19:01、Xiaodi Wu via swift-evolution <swift-evolution@swift.org>
On Wed, Jul 20, 2016 at 8:10 PM, Robert Widmann <rwidmann@apple.com> >> wrote:

On Jul 20, 2016, at 5:47 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Wed, Jul 20, 2016 at 6:30 PM, Robert Widmann <rwidmann@apple.com> >>> wrote:

Or better yet: we can offer diagnostics about this case just as we can
offer diagnostics for doubled imports or improperly nested imports. A big
part of why this proposal exists is because the existing diagnostics around
qualified imports are uninformative and unhelpful.

Now reread the chaining example in the proposal.

import Swift using (String hiding (UTF8View), Int, Double)

A qualified import is defining a procedure to import a subset of

identifiers. That’s it.

Right, and I think an entirely different way of thinking about this
would be much easier to learn and teach. Whether using, hiding, and
renaming are to be supported now, later, or never, my mental picture of how
it fits together is quite simple:

Analogy--suppose I am a pickle merchant. I import Foo-branded pickles
from vendor X. I must re-label them with the right nutritional information
before I can sell in this country. I can have labels printed saying that
they are Foo-branded pickles. I can have them branded as Bar-branded
pickles. Or I can have the labels deliberately misprinted, and then these
pickles will never see the light of day. Point is, each of these is an
active choice; even if I sell these as Bar-branded pickles, it's not that
these pickles reached the domestic market as Foo-branded pickles, after
which I scratched out the label with a Sharpie. These pickles had no
domestic brand until I gave it one.

Back to importing modifiers--I import type Foo from module X. In my
code, I need to make a choice to call this type Foo, or Bar, or nothing at
all. In other words, there is only one directive, importing, and I am
importing `Foo as Foo`, `Foo as Bar`, or `Foo as _`. Meanwhile, `import X
using Foo` or `import X.Foo` (whatever the color of the bikeshed) would
just be a shorthand for `import X using Foo as Foo` or `import X.Foo as
Foo`. In this conceptualization, if I choose to import Foo as Bar, it's not
that I'm importing Foo into the scope, then changing the identifier to Bar.
The only identifier it ever has in this scope is Bar.

And I’m the one with the complex semantics? :)

I'm just trying to put into words what I'm familiar with after working in
other languages such as Python.

Python may not be the right mindset for this. Their import story is much
simpler because of their module system and generally simpler programming
model.

How about this:

Using and Hiding relate to each other the way && and || do for bools.
If && can be said to “prefer to return false, but return true given no
other alternative” and || can be said to “prefer returning true, but return
false given no other alternative”, then hiding can be said to “prefer
importing all identifiers unless told not to in specific instances” and
using can be said to “prefer importing no identifiers unless told to in
specific instances”.

import Module.Name using (A, B, C, …) === import Module.Name hiding
(ALL_NAMES - {A, B, C, ...})
import Module.Name hiding (A, B, C, …) === import Module.Name using
(ALL_NAMES - {A, B, C, ...})

That seems a particularly simple explanation to me. Let me know if
anything else is unclear.

Your mental framework is clear. It's one that's just not found in very
many other languages. Many of these have import declarations (or similar)
with simpler syntax, yet they seem to address at least some of the problems
that motivate your proposal. I guess my question in the end is, why have
you chosen Agda as the basis for qualified imports in Swift and not one of
these other languages?

I chose Agda because it's the only major language I could find that
treated identifiers like notation and used its module system for *real* organization
of code; it's just a name, it can change if you want it to. The entire
language is flexible. You can redefine functions with just an =, you can
give new syntactic transformations without having to write crazy macros or
worrying about hygiene. You get so much support from the type system too
that all of these features just work together and you can sit back and feel
satisfied that your tools pushed you to write *good code. *I wrote it
with Agda in mind because they took the time to think about the
interactions between modules, code, and scope in a way we just haven't had
the time to yet.

Many of these imports have simpler syntax but don't chain so lose
expressiveness (Java). Or may have simpler surface syntax but don't
properly interact with a moduleless system (Python, Java). Or may have a
simpler syntax and give way to ambiguities (Python) when used in Swift.
Agda is unambiguous, scalable, extensible, and simple.

Please don't confuse what's new with what's complex. But at the same
time if there are any unexplainable loopholes we should know about them. I
just haven't heard much I couldn't point to the proposal about yet so I
don't see a reason to change.

Thanks for this insight. It's clear that you're aiming at a far richer
system in the future. However, it's hard to gauge how far Swift will
eventually scale/extend what you're proposing here. I don't think there's
any denying that the solution you've chosen introduces more involved
semantics and a more verbose syntax than what's strictly necessary to
address the specific motivating problem you give in this particular
instance. Without a better sense of the outer bounds of how far this system
will eventually be pushed, it's hard to judge whether the eventual payoff
will be "worth it." And without any mention of the grander plan, I suspect
the feedback you're going to get will continue along the path of trying to
tear away whatever you're trying to put in place for the future which is
not discernibly necessary for solving the immediate problem at hand.

If you believe this proposal is not extensible then cite a particular
example, otherwise I can only say that I can’t tell the future. The syntax
presented here is inspired by a tiny, tiny fragment of a language with an
incredibly rich import mechanism and module system. It’s unclear how much
of that system fits with Swift current - we already tried to formalize
quite a bit in the first draft. For right now, *this* proposal is trying
to clean up a long-neglected part of the language. If you feel that we’ve
failed in that regard then tell me. Otherwise I’m fine with my syntax
being pulled in a different direction in future additive proposals. I just
want to fix this part of the language before the window on source-breaking
changes closes for a while.

On Jul 20, 2016, at 4:17 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Wed, Jul 20, 2016 at 4:57 PM, Robert Widmann <rwidmann@apple.com> >>>> wrote:

On Jul 20, 2016, at 2:52 PM, Robert Widmann via swift-evolution < >>>>> swift-evolution@swift.org> wrote:

On Jul 20, 2016, at 2:35 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Wed, Jul 20, 2016 at 4:24 PM, Robert Widmann <rwidmann@apple.com> >>>>> wrote:

On Jul 20, 2016, at 2:19 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Wed, Jul 20, 2016 at 4:06 PM, Robert Widmann <rwidmann@apple.com> >>>>>> wrote:

On Jul 20, 2016, at 2:04 PM, Robert Widmann via swift-evolution < >>>>>>> swift-evolution@swift.org> wrote:

On Jul 20, 2016, at 1:59 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Why is hiding in-scope but renaming out-of-scope?

Because hiding and renaming can be used in combination to subset out
APIs, not alter them.

I mistyped. Should be "Because hiding and using can be used in
combination to subset out APIs, not alter them."

Sure, I buy that.

Both are additive to Swift,

As part of this proposal, both are source-breaking.

I don't see how. If hiding were cut from the proposal, adding it
later with even the exact syntax you propose should break no pre-existing
code--am I wrong?

Renaming the way we originally laid it out would certainly be
additive. The way you have it laid out would overlap a bit with hiding,
sure, but it is still additive and (IMO, but I’m probably among a tiny
minority of users that has used a proof assistant’s syntax as the basis for
a proposal!) a good thing to have.

Sorry, I fear I've incorrectly communicated the point I was trying to
make. I'm not advocating here for inclusion of renaming as part of this
proposal. I simply think that--even though I buy your claim that hiding and
using both subset out APIs--hiding has more affinity with renaming and the
two facilities probably ought to be considered together, whenever that is.

Thus, I'm suggesting that it would be feasible to postpone discussion
of hiding until such future time as a fully fleshed out renaming scheme is
proposed. A revamped source-breaking import syntax without either hiding or
renaming could be put in place now, and future addition of hiding and/or
renaming would not have to be source-breaking. Is there something wrong
with this argument?

There is still a useful to distinction to be made between explicitly
renaming an API and explicitly hiding an API. Scala’s syntax to rename to
underbar is a convenient notation for that kind of thing, but it goes
against making qualified imports explicit and it means that renaming
necessarily has to import identifiers into scope as well as rename them.
What the OP (maybe it was you, sorry if it was) meant by “equivalent”
missed the point that

import Swift hiding (String)

doesn’t translate into

import Swift renaming (String, to: _)

it translates into

import Swift hiding () renaming (String, to: _)

Renaming introducing identifiers into scope seems like a phase-shift
and is not something the verb “rename” implies should happen here. It’s an
interesting little hole in Agda’s module system that you can use

open A hiding (xs) renaming (ys to zs)

to mean

open A using (A; xs; ys) renaming (ys to zs)

Actually, scratch that. Their documentation explicitly mentions that
hiding and renaming may not be mixed because of the phase distinction and
recommend the using translation above as the way to go.

This is very illuminating. I think I've rather misunderstood what it is
you're proposing. I wonder if others did also.

The syntax you proposed seemed cumbersome to me because my mental model
of importing (informed by my probably superficial understanding of vanilla
procedural programming languages) has only one phase: importing. This is
why I proposed radically simplifying the spelling. To me, all of these
operations are just sugar on a single import phase, where "stuff" from
outside the module is "brought into" the module, either with the same name
("using"), a different name ("renaming"), or no name ("hiding").

But what you're saying here--if I understand correctly--is that you're
proposing a multi-phase import system, where the possible phases, which can
be composed in varying orders, are "using", "hiding", and "renaming". This
is much, much more elaborate than I had contemplated. So beyond the
bikeshedding of syntax, I'd ask: why do we need this multi-phase model of
importing?

and as has been argued by others, the former is a special case of the

latter.

A special case that cannot cause large-scale file-relative changes
to APIs. Renaming is primarily used in other languages that treat free
functions as more canonical than we do, or allow operator definitions that
can be used as notation.

I don't know about 'primary use,' but the most common use I've
experienced in Python, for example, is the mundane task of importing module
Foo2 as Foo.

And I still want that kind of syntax. I just want to get the
breaking changes out of the way to make room for it in the future.

Right. See above about my argument as to which parts of your proposal
have to be source-breaking, and which don't.

In those cases, you often have your own notation you’d like to use.

In Swift, such changes should be rare enough that if you can’t solve them
with a disambiguating qualified import then you can just redeclare the
identifier some other way (typealias, top-level let, wrapper class,
whatever).

You've already stripped out renaming of members from the proposal. I
agree wholeheartedly. The only flavor of renaming I'm thinking of here is
equivalent to a fileprivate typealias and hiding, which cannot be done in
this version of the proposal because hiding always comes before
typealiasing and you can't typealias what isn't imported. It isn't about
altering APIs any more than a fileprivate typealias can be thought of as
altering APIs.

In the sense that you can’t use the original identifier if you rename
it, it is an alteration. John brought up a great point about exporting
these things and how it could be a potentially dangerous thing. Even used
locally, there’s the potential for people to specify 500 lines of import
renaming crap that has to be copypasta’d throughout the codebase to
maintain that particular style - not a use-case I’ve ever seen, but the
potential is there.

This is, I think, a spurious argument. I can equally have 500 lines of
private typealiased crap that has to be copypasta'd.

On Wed, Jul 20, 2016 at 15:55 Brandon Knope <bknope@me.com> wrote:

I meant is there any reason for requiring parentheses

On Jul 20, 2016, at 4:53 PM, Robert Widmann <rwidmann@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@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@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@me.com> wrote:

On Jul 20, 2016, at 3:08 PM, Xiaodi Wu via swift-evolution < >>>>>>>>> swift-evolution@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@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
<https://github.com/apple/swift-evolution/pull/440&gt;, or as a gist
<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6&gt;
.

Thanks,

~Robert Widmann

Qualified Imports Revisited

   - Proposal: SE-NNNN
   <https://gist.github.com/CodaFi/NNNN-first-class-qualified-imports.md&gt;
   - Authors: Robert Widmann <https://github.com/codafi&gt;, TJ
   Usiyan <https://github.com/griotspeak&gt;
   - Status: Awaiting review
   - Review manager: TBD

<qualified-imports.md · GitHub;
Introduction

We propose a complete overhaul of the qualified imports syntax
and semantics.

<qualified-imports.md · GitHub;
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.

<qualified-imports.md · GitHub
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.

<qualified-imports.md · GitHub
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.DateComponentsimport 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: "...") // Validvar 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.UTF8Viewimport 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.

<qualified-imports.md · GitHub
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.

<qualified-imports.md · GitHub
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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I know that the compiler can't read my mind. I don't mean to be frustrating.

If Foundation is imported in the bridging header of a project (as it is today, by default, in Swift framework projects), you get all of Foundation in all of your Swift code without a chance to write "import Foundation hiding (...)" anywhere. This reinforces my position that types should be selected affirmatively.

I'm not proposing a better solution because I don't have one right now. But if it would help appease you that I suggest something: keep "import using", let go of "hiding", and reuse the "using" keyword to select symbols from specific modules to break ambiguities. (Names must always be module-qualified to break ambiguities with classes that have the same name as a module.)

import Foo
import Bar

using Foo.Baz; // in case of ambiguity, pick Foo.Baz
using extension Bar.Date; // in case of ambiguity on any Date extension method, pick the one in Bar
using extension Foo.Date.frob; // except for frob, that one comes from Foo

I know that this has problems on its own. Notably, `using` can't be a contextual keyword, the identifier now has to be reserved.

Félix

···

Le 20 juil. 2016 à 23:51:15, Robert Widmann <rwidmann@apple.com> a écrit :

On Jul 20, 2016, at 9:37 PM, Félix Cloutier via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

The problem is that by specifying "import Foo using (Baz)", I get nothing else from Foo. If I only want to exclude one conflicting name, I would have:

import Foo
import Bar hiding (Baz)

In case of a conflict, my "internal monologue" is more like "take Baz from Foo" than "don't take Baz from Bar".

How else would you resolve an ambiguity than by selecting the appropriate declaration and hiding the others? Swift’s semantic analysis cannot read your mind, and neither can your (or mine) proposal for renaming syntax - in that you still have to import both modules either way. You may as well be explicit about which name you’re actually using and which ones you’re actually hiding, eh?

Félix

Le 20 juil. 2016 à 20:46:18, Robert Widmann <devteam.codafi@gmail.com <mailto:devteam.codafi@gmail.com>> a écrit :

~Robert Widmann

2016/07/20 20:07、Félix Cloutier via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> のメッセージ:

My understanding is that we want "using" and "hiding" because we want the ability to either take just a few things OR leave out just a few things. With a unified "import Foo (A = B, C = D, E = _) syntax, we only get the ability to take a few things AND hide a few things.

Again, renaming is not something I want done with the same syntax as introducing and removing things from scope because they are distinct operations. This import tells me nothing from a semantic perspective and just seems easy to type rather than understand. Nowhere in the language can you find something that resembles this either.

I've never really been into a case where I badly had to *not* import a name, so while I see why it makes sense from a mathematical perspective to have "hiding", I'm not sure how much I'd miss it if it wasn't there.

I realize that it solves the ambiguous type problem <Issues · apple/swift-issues · GitHub, but I consider that it's a flawed solution. Instead of specifying from which module you want an import, you have to specify on which modules you don't want it.

You still specify which module you want to import from, so I don't see your point here. Given that Foo and Bar both define a class Baz, here's your import

import Foo using (Baz)
import Bar hiding (Baz)

What's the problem here? Isn't this exactly what you wanted to say in English (or whatever internal monologue you might have) spelled out in code? It scales immediately to multiple ambiguities and we can provide diagnostics to insert or remove identifiers in these lists to help the user out when they get stuck with an insufficiently inclusive or exclusive import list. The Python example is much more difficult to reason about from my perspective and from the perspective of the compiler. In fact, it's almost the code that's needed today to work around this problem - we're trying to fix the need for this here.

To see if we can get inspiration, I'd like to pitch an imperfect Python-like approach, where you could import a module as a namespace (and then you'd always have to write Module.Class, with modules systematically shadowing classes in the global namespace), or in addition to that, import every top-level name in the module into the file's global namespace. Names defined in multiple modules remain ambiguous unless explicitly shadowed:

private typealias OrderedSet = BTree.OrderedSet
private var foo: (Int) -> Int = Bar.foo

You would not be allowed to shadow a module with a class.

This, however, still does not solve the extension problem. Additionally, given that the default visibility for top-level names is internal, careless users could easily pollute the project's global namespace. Finally, for micro-frameworks that have a class with the same name as a module, you'd always have to write Name.Name, since the class can't shadow the module.

Félix

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

Simple! You don't need to hide the others if we enforce a rule that explicitly mentioning one in the current file imports that name as a fileprivate identifier which shadows the ones not explicitly mentioned:

    import A using (X)
    import B // also exports X which gets shadowed by A.X
    import C // also exports X which gets shadowed by A.X

    assert(X.self == A.X.self)
    assert(X.self != B.X.self)
    assert(X.self != C.X.self)
    
    import D using (X)
    // error: invalid redeclaration of 'X'
    // note: previously declared here: 'import A using (X)'
    
    typealias X = Int
    // error: invalid redeclaration of 'X'
    // note: previously declared here: 'import A using (X)'

That would go nicely hand-in-hand with the idea that explicitly importing a module with a qualified name brings in the name of that module to the current file's scope:

    import A // ok, unqualified import keeps A as a second-class identifier
    import B as B // ok, qualified import makes B a first-class identifier in file scope
    
    typealias A = Int // ok, shadows the module name A
    
    typealias B = Int
    // error: invalid redeclaration of 'Bar'
    // note: previously declared here: 'import Bar as Bar'

Couldn't we find a synthesis with both the explicit qualification of modules and the explicit selection of imported names? I would strongly support that.

— Pyry

···

On 21 Jul 2016, at 09:51, Robert Widmann via swift-evolution <swift-evolution@swift.org> wrote:

On Jul 20, 2016, at 9:37 PM, Félix Cloutier via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

The problem is that by specifying "import Foo using (Baz)", I get nothing else from Foo. If I only want to exclude one conflicting name, I would have:

import Foo
import Bar hiding (Baz)

In case of a conflict, my "internal monologue" is more like "take Baz from Foo" than "don't take Baz from Bar".

How else would you resolve an ambiguity than by selecting the appropriate declaration and hiding the others? Swift’s semantic analysis cannot read your mind, and neither can your (or mine) proposal for renaming syntax - in that you still have to import both modules either way. You may as well be explicit about which name you’re actually using and which ones you’re actually hiding, eh?

~Robert Widmann

2016/07/20 19:01、Xiaodi Wu via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> のメッセージ:

“The Phase Distinction” is a semantic one, not one built into the import system itself.

I understand. To rephrase my question: why introduce this semantic distinction to Swift?

What I meant is that even the system I’m modeling this on makes a distinction between import directives that actually expose identifiers to modules and import directives that modify identifiers that are already in scope.

This is, IMO, very complex. I appreciate enormously the conceptual simplicity of the current Swift approach which, for all of its deficiencies, has only one import directive that does what it says on the tin: it exposes identifiers. I'm not bothered if it gains the ability to expose identifiers differently from one file to the next without keywords firewalled from each other to preserve the notion of phases of import.

We are not changing the unqualified Swift import system. Take a gander at the proposal again, or even the first draft. Swift has a particularly strange syntax for qualified imports that hasn’t received attention since it was first introduced 2 major versions ago. That thing allows quite a variety of senseless variants that can be both completely expressed by and subsumed by `using` and `hiding`.

My sense, which I think has been echoed by others, is that the proposed solution is syntactically complex, and now that I understand that you're thinking through a multi-phase concept, also conceptually multilayered. I'm not arguing that the existing syntax for qualified imports doesn't need changing, only that there is room for radical simplification of the proposed solution IMO. As I re-read this proposal once more, it strikes me that the motivating issues identified (needlessly specific, does not compose, etc.) don't clearly argue for the specific direction proposed as opposed to alternatives like Joe's.

Perhaps they need to reread the proposal. Syntactically complex how? We're introducing two keywords and using tuple syntax. Our grammar changes are laid bare and take up 7 lines. I think there might be a general air of confusing semantics changes with syntax changes.

I can't speak for the general air, but putting on my disinterested reader hat, I can see why the confusion might arise--

The Motivation section begins:
"The existing syntax for qualified imports..."

And the Proposed Solution begins:
"The grammar and semantics of qualified imports..."

But the Detailed Design begins:
"Qualified import syntax will be revised..."

It's neither here nor there in terms of the proposal content, but suffice it to say that if one strings together the topic sentences in your proposal, the overarching narrative to be gleaned here is: "The current syntax for qualified imports is no good; therefore, we revise the semantics of qualified imports by changing the syntax." Sure.

Ne’er the twain shall meet.

Yes, you explained this concept very clearly as it applies to Agda. But I just don't see why we should care to have this distinction. Yet you are very adamant about it. What am I missing?

We should care because that is precisely what the two operations do. `using` and `hiding` introduce things or remove things from scope which is a very different operation from taking something that is already in scope and giving it a new name.

Perhaps I'm not phrasing my question very cogently. Of course, if we are to have `using`, `hiding`, and `renaming`, we must observe the distinctions between them.

If you don’t want to think of them as part of the same import process, think of them instead in terms of their Swift equivalents today.

import Foundation using (Date) == import struct Foundation.Date
import Foundation hiding (Date) == import Foundation; @unavailable(*, “…") typealias Date = Foundation.Date
import Foundation using (TimeInterval) renaming (TimeInterval, to: Time) == import typealias Foundation.TimeInterval; typealias Time = Foundation.TimeInterval

Notice how it takes two declarations to create a renaming? It is not simple to drop being explicit about which names are actually in scope and expect a renaming to just implicitly slip in a using declaration. Nor is it simple to imagine _ as some magical namespace that you can pack away unwanted definitions into. using and hiding are very physical things and the rules for their behavior should be obvious and unambiguous - the proposal contains some examples of valid and invalid declarations to help with that.

The examples worry me, in fact. That we might need to contemplate the behavior of a statement such as `import Foo using () hiding () hiding () using () hiding ()` suggests it's perhaps a little over-engineered for the purpose. Why allow chaining of `using` and `hiding` anyway? The only example given is of a nested type, which suggests nesting would be the way to go:

We allow chaining specifically to avoid that nesting behavior. Let's break up that chained import line by line to see why

import Swift using (String, Int, Double, Character)
                      hiding (String.UTF8View)

import Swift // Scope contains {all identifiers in Swift}
using (String, Int, Double, Character) // Scope contains {String.*, Int.*, Double.*, Character.*}
                      hiding (String.UTF8View) // Scope contains {{String.* - String.UTF8View.*}, Int.*, Double.*, Character.*}

We express the exact same example in a much more human-readable way. You can read this out loud in plain English if you don't believe me: "import everything in Swift that is in String, Int, Double, and Character except String.UTF8View"

I have to disagree with your reasoning here. Unless I'm mistaken, the readability we're most concerned with is that of the written text, not its spoken form.

Nesting is an almost exclusively _visual_ way of organizing text and it adds real clarity on the page. Of course, if your litmus test for readability is literally trying to read it out loud, you would conclude that nesting is inferior. However, you'd make that same conclusion about so many other choices in Swift syntax (take, for instance, `:` instead of `extends` or `->` instead of `returns`). I conclude, on the other hand, that Swift is clearly not aiming to be AppleScript-like in this respect.

I disagree. Standard Library Swift reads left to right in a very carefully chosen and consistent manner - it has been refined on this list and by the team quite a lot to get it that way. You seem to think I’m arguing verbosity is the end goal (AppleScript?): I’m not. Clarity is the goal. It is unambiguous what you mean when you say

import Swift using (String, Int, Double) hiding (String.UTF8View)

Is it ambiguous to say `import Swift using (String hiding (UTF8View), Int, Double)`?

What happens when it is time to extend this proposal to members?

This syntax fits well with the overall direction of the language itself. For example, I use whitespace here out of habit but here’s nothing stopping you from making these look like the (meta)function invocations they really are

import Swift
  using(String, Int, Double)
  hiding(String.UTF8View)

In the comments you've used the nested notation `{{String.* - String.UTF8View.*}, Int.*, Double.*, Character.*}` to explain what it is your proposed syntax means. I really do find that, with all the punctuation in it, clearer than the chained notation. Of course, written `(String hiding UTF8View), Int, Double, Character`, it would be clearer still.

Critically, it eliminates the possibility of absurd chains of `hiding () using () hiding () using ()`, which create many more ways to represent the same subset of imports than can a nested syntax.

If you wish to express this, why should the language stop you?

Given two options for syntax, one where expressing this chain is never necessary or even possible, and another where it is possible and perhaps even necessary, the former wins in terms of clarity, no?

Yours does not exclude this possibility, it merely shuffles it around (unless you plan on banning the empty tuple. In which case, why?)

import Swift using (String using(UTF8View hiding ()), Int hiding (), Double hiding())

At this point we’re just circling around an edge case described in the proposal as ripe for a merge-by-diagnostic.

···

On Jul 20, 2016, at 11:56 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Thu, Jul 21, 2016 at 1:40 AM, Robert Widmann <rwidmann@apple.com <mailto:rwidmann@apple.com>> wrote:

On Jul 20, 2016, at 10:04 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:
On Wed, Jul 20, 2016 at 10:33 PM, Robert Widmann <devteam.codafi@gmail.com <mailto:devteam.codafi@gmail.com>> wrote:

On Wed, Jul 20, 2016 at 8:10 PM, Robert Widmann <rwidmann@apple.com <mailto:rwidmann@apple.com>> wrote:

On Jul 20, 2016, at 5:47 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:
On Wed, Jul 20, 2016 at 6:30 PM, Robert Widmann <rwidmann@apple.com <mailto:rwidmann@apple.com>> wrote:

Or better yet: we can offer diagnostics about this case just as we can offer diagnostics for doubled imports or improperly nested imports. A big part of why this proposal exists is because the existing diagnostics around qualified imports are uninformative and unhelpful.

Now reread the chaining example in the proposal.

import Swift using (String hiding (UTF8View), Int, Double)

A qualified import is defining a procedure to import a subset of identifiers. That’s it.

Right, and I think an entirely different way of thinking about this would be much easier to learn and teach. Whether using, hiding, and renaming are to be supported now, later, or never, my mental picture of how it fits together is quite simple:

Analogy--suppose I am a pickle merchant. I import Foo-branded pickles from vendor X. I must re-label them with the right nutritional information before I can sell in this country. I can have labels printed saying that they are Foo-branded pickles. I can have them branded as Bar-branded pickles. Or I can have the labels deliberately misprinted, and then these pickles will never see the light of day. Point is, each of these is an active choice; even if I sell these as Bar-branded pickles, it's not that these pickles reached the domestic market as Foo-branded pickles, after which I scratched out the label with a Sharpie. These pickles had no domestic brand until I gave it one.

Back to importing modifiers--I import type Foo from module X. In my code, I need to make a choice to call this type Foo, or Bar, or nothing at all. In other words, there is only one directive, importing, and I am importing `Foo as Foo`, `Foo as Bar`, or `Foo as _`. Meanwhile, `import X using Foo` or `import X.Foo` (whatever the color of the bikeshed) would just be a shorthand for `import X using Foo as Foo` or `import X.Foo as Foo`. In this conceptualization, if I choose to import Foo as Bar, it's not that I'm importing Foo into the scope, then changing the identifier to Bar. The only identifier it ever has in this scope is Bar.

And I’m the one with the complex semantics? :)

I'm just trying to put into words what I'm familiar with after working in other languages such as Python.

Python may not be the right mindset for this. Their import story is much simpler because of their module system and generally simpler programming model.

How about this:

Using and Hiding relate to each other the way && and || do for bools. If && can be said to “prefer to return false, but return true given no other alternative” and || can be said to “prefer returning true, but return false given no other alternative”, then hiding can be said to “prefer importing all identifiers unless told not to in specific instances” and using can be said to “prefer importing no identifiers unless told to in specific instances”.

import Module.Name using (A, B, C, …) === import Module.Name hiding (ALL_NAMES - {A, B, C, ...})
import Module.Name hiding (A, B, C, …) === import Module.Name using (ALL_NAMES - {A, B, C, ...})

That seems a particularly simple explanation to me. Let me know if anything else is unclear.

Your mental framework is clear. It's one that's just not found in very many other languages. Many of these have import declarations (or similar) with simpler syntax, yet they seem to address at least some of the problems that motivate your proposal. I guess my question in the end is, why have you chosen Agda as the basis for qualified imports in Swift and not one of these other languages?

I chose Agda because it's the only major language I could find that treated identifiers like notation and used its module system for real organization of code; it's just a name, it can change if you want it to. The entire language is flexible. You can redefine functions with just an =, you can give new syntactic transformations without having to write crazy macros or worrying about hygiene. You get so much support from the type system too that all of these features just work together and you can sit back and feel satisfied that your tools pushed you to write good code. I wrote it with Agda in mind because they took the time to think about the interactions between modules, code, and scope in a way we just haven't had the time to yet.

Many of these imports have simpler syntax but don't chain so lose expressiveness (Java). Or may have simpler surface syntax but don't properly interact with a moduleless system (Python, Java). Or may have a simpler syntax and give way to ambiguities (Python) when used in Swift. Agda is unambiguous, scalable, extensible, and simple.

Please don't confuse what's new with what's complex. But at the same time if there are any unexplainable loopholes we should know about them. I just haven't heard much I couldn't point to the proposal about yet so I don't see a reason to change.

Thanks for this insight. It's clear that you're aiming at a far richer system in the future. However, it's hard to gauge how far Swift will eventually scale/extend what you're proposing here. I don't think there's any denying that the solution you've chosen introduces more involved semantics and a more verbose syntax than what's strictly necessary to address the specific motivating problem you give in this particular instance. Without a better sense of the outer bounds of how far this system will eventually be pushed, it's hard to judge whether the eventual payoff will be "worth it." And without any mention of the grander plan, I suspect the feedback you're going to get will continue along the path of trying to tear away whatever you're trying to put in place for the future which is not discernibly necessary for solving the immediate problem at hand.

If you believe this proposal is not extensible then cite a particular example, otherwise I can only say that I can’t tell the future. The syntax presented here is inspired by a tiny, tiny fragment of a language with an incredibly rich import mechanism and module system. It’s unclear how much of that system fits with Swift current - we already tried to formalize quite a bit in the first draft. For right now, this proposal is trying to clean up a long-neglected part of the language. If you feel that we’ve failed in that regard then tell me. Otherwise I’m fine with my syntax being pulled in a different direction in future additive proposals. I just want to fix this part of the language before the window on source-breaking changes closes for a while.

On Jul 20, 2016, at 4:17 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:

On Wed, Jul 20, 2016 at 4:57 PM, Robert Widmann <rwidmann@apple.com <mailto:rwidmann@apple.com>> wrote:

On Jul 20, 2016, at 2:52 PM, Robert Widmann via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jul 20, 2016, at 2:35 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:

On Wed, Jul 20, 2016 at 4:24 PM, Robert Widmann <rwidmann@apple.com <mailto:rwidmann@apple.com>> wrote:

On Jul 20, 2016, at 2:19 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:

On Wed, Jul 20, 2016 at 4:06 PM, Robert Widmann <rwidmann@apple.com <mailto:rwidmann@apple.com>> wrote:

On Jul 20, 2016, at 2:04 PM, Robert Widmann via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jul 20, 2016, at 1:59 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:

Why is hiding in-scope but renaming out-of-scope?

Because hiding and renaming can be used in combination to subset out APIs, not alter them.

I mistyped. Should be "Because hiding and using can be used in combination to subset out APIs, not alter them."

Sure, I buy that.

Both are additive to Swift,

As part of this proposal, both are source-breaking.

I don't see how. If hiding were cut from the proposal, adding it later with even the exact syntax you propose should break no pre-existing code--am I wrong?

Renaming the way we originally laid it out would certainly be additive. The way you have it laid out would overlap a bit with hiding, sure, but it is still additive and (IMO, but I’m probably among a tiny minority of users that has used a proof assistant’s syntax as the basis for a proposal!) a good thing to have.

Sorry, I fear I've incorrectly communicated the point I was trying to make. I'm not advocating here for inclusion of renaming as part of this proposal. I simply think that--even though I buy your claim that hiding and using both subset out APIs--hiding has more affinity with renaming and the two facilities probably ought to be considered together, whenever that is.

Thus, I'm suggesting that it would be feasible to postpone discussion of hiding until such future time as a fully fleshed out renaming scheme is proposed. A revamped source-breaking import syntax without either hiding or renaming could be put in place now, and future addition of hiding and/or renaming would not have to be source-breaking. Is there something wrong with this argument?

There is still a useful to distinction to be made between explicitly renaming an API and explicitly hiding an API. Scala’s syntax to rename to underbar is a convenient notation for that kind of thing, but it goes against making qualified imports explicit and it means that renaming necessarily has to import identifiers into scope as well as rename them. What the OP (maybe it was you, sorry if it was) meant by “equivalent” missed the point that

import Swift hiding (String)

doesn’t translate into

import Swift renaming (String, to: _)

it translates into

import Swift hiding () renaming (String, to: _)

Renaming introducing identifiers into scope seems like a phase-shift and is not something the verb “rename” implies should happen here. It’s an interesting little hole in Agda’s module system that you can use

open A hiding (xs) renaming (ys to zs)

to mean

open A using (A; xs; ys) renaming (ys to zs)

Actually, scratch that. Their documentation explicitly mentions that hiding and renaming may not be mixed because of the phase distinction and recommend the using translation above as the way to go.

This is very illuminating. I think I've rather misunderstood what it is you're proposing. I wonder if others did also.

The syntax you proposed seemed cumbersome to me because my mental model of importing (informed by my probably superficial understanding of vanilla procedural programming languages) has only one phase: importing. This is why I proposed radically simplifying the spelling. To me, all of these operations are just sugar on a single import phase, where "stuff" from outside the module is "brought into" the module, either with the same name ("using"), a different name ("renaming"), or no name ("hiding").

But what you're saying here--if I understand correctly--is that you're proposing a multi-phase import system, where the possible phases, which can be composed in varying orders, are "using", "hiding", and "renaming". This is much, much more elaborate than I had contemplated. So beyond the bikeshedding of syntax, I'd ask: why do we need this multi-phase model of importing?

and as has been argued by others, the former is a special case of the latter.

A special case that cannot cause large-scale file-relative changes to APIs. Renaming is primarily used in other languages that treat free functions as more canonical than we do, or allow operator definitions that can be used as notation.

I don't know about 'primary use,' but the most common use I've experienced in Python, for example, is the mundane task of importing module Foo2 as Foo.

And I still want that kind of syntax. I just want to get the breaking changes out of the way to make room for it in the future.

Right. See above about my argument as to which parts of your proposal have to be source-breaking, and which don't.

In those cases, you often have your own notation you’d like to use. In Swift, such changes should be rare enough that if you can’t solve them with a disambiguating qualified import then you can just redeclare the identifier some other way (typealias, top-level let, wrapper class, whatever).

You've already stripped out renaming of members from the proposal. I agree wholeheartedly. The only flavor of renaming I'm thinking of here is equivalent to a fileprivate typealias and hiding, which cannot be done in this version of the proposal because hiding always comes before typealiasing and you can't typealias what isn't imported. It isn't about altering APIs any more than a fileprivate typealias can be thought of as altering APIs.

In the sense that you can’t use the original identifier if you rename it, it is an alteration. John brought up a great point about exporting these things and how it could be a potentially dangerous thing. Even used locally, there’s the potential for people to specify 500 lines of import renaming crap that has to be copypasta’d throughout the codebase to maintain that particular style - not a use-case I’ve ever seen, but the potential is there.

This is, I think, a spurious argument. I can equally have 500 lines of private typealiased crap that has to be copypasta'd.

On Wed, Jul 20, 2016 at 15:55 Brandon Knope <bknope@me.com <mailto:bknope@me.com>> wrote:
I meant is there any reason for requiring parentheses

On Jul 20, 2016, at 4:53 PM, Robert Widmann <rwidmann@apple.com <mailto:rwidmann@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@me.com <mailto:bknope@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@gmail.com <mailto:xiaodi.wu@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@me.com <mailto:bknope@me.com>> wrote:

On Jul 20, 2016, at 3:08 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@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@swift.org <mailto:swift-evolution@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 <https://github.com/apple/swift-evolution/pull/440&gt;, or as a gist <https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6&gt;\.

Thanks,

~Robert Widmann

Qualified Imports Revisited

Proposal: SE-NNNN <https://gist.github.com/CodaFi/NNNN-first-class-qualified-imports.md&gt;
Authors: Robert Widmann <https://github.com/codafi&gt;, TJ Usiyan <https://github.com/griotspeak&gt;
Status: Awaiting review
Review manager: TBD

<qualified-imports.md · GitHub

We propose a complete overhaul of the qualified imports syntax and semantics.

<qualified-imports.md · GitHub

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.

<qualified-imports.md · GitHub 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.

<qualified-imports.md · GitHub 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.

<qualified-imports.md · GitHub 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.

<qualified-imports.md · GitHub 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@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

~Robert Widmann

2016/07/21 0:13、Pyry Jahkola via swift-evolution <swift-evolution@swift.org> のメッセージ:

The problem is that by specifying "import Foo using (Baz)", I get nothing else from Foo. If I only want to exclude one conflicting name, I would have:

import Foo
import Bar hiding (Baz)

In case of a conflict, my "internal monologue" is more like "take Baz from Foo" than "don't take Baz from Bar".

How else would you resolve an ambiguity than by selecting the appropriate declaration and hiding the others? Swift’s semantic analysis cannot read your mind, and neither can your (or mine) proposal for renaming syntax - in that you still have to import both modules either way. You may as well be explicit about which name you’re actually using and which ones you’re actually hiding, eh?

Simple! You don't need to hide the others if we enforce a rule that explicitly mentioning one in the current file imports that name as a fileprivate identifier which shadows the ones not explicitly mentioned:

What you're describing is making a distinction between an open module and an imported module and is both additive and out of scope for this particular proposal. We didn't want to touch module re-exports until that proposal came up later. This is a fantastic idea that we have plans to incorporate in there nonetheless. This distinction is far more powerful than our current approach of just opening every module that gets imported into the top level namespace.

···

On 21 Jul 2016, at 09:51, Robert Widmann via swift-evolution <swift-evolution@swift.org> wrote:
On Jul 20, 2016, at 9:37 PM, Félix Cloutier via swift-evolution <swift-evolution@swift.org> wrote:

    import A using (X)
    import B // also exports X which gets shadowed by A.X
    import C // also exports X which gets shadowed by A.X

    assert(X.self == A.X.self)
    assert(X.self != B.X.self)
    assert(X.self != C.X.self)
    
    import D using (X)
    // error: invalid redeclaration of 'X'
    // note: previously declared here: 'import A using (X)'
    
    typealias X = Int
    // error: invalid redeclaration of 'X'
    // note: previously declared here: 'import A using (X)'

That would go nicely hand-in-hand with the idea that explicitly importing a module with a qualified name brings in the name of that module to the current file's scope:

    import A // ok, unqualified import keeps A as a second-class identifier
    import B as B // ok, qualified import makes B a first-class identifier in file scope
    
    typealias A = Int // ok, shadows the module name A
    
    typealias B = Int
    // error: invalid redeclaration of 'Bar'
    // note: previously declared here: 'import Bar as Bar'

Couldn't we find a synthesis with both the explicit qualification of modules and the explicit selection of imported names? I would strongly support that.

— Pyry

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

~Robert Widmann

2016/07/21 8:17、Félix Cloutier <felixcca@yahoo.ca> のメッセージ:

I know that the compiler can't read my mind. I don't mean to be frustrating.

If Foundation is imported in the bridging header of a project (as it is today, by default, in Swift framework projects), you get all of Foundation in all of your Swift code without a chance to write "import Foundation hiding (...)" anywhere. This reinforces my position that types should be selected affirmatively.

I don't understand this at all. The point of using and hiding is we create a file-local subset of identifiers you want to see. Bridging headers do not change that. I don't see how this is an example that affirmative is the *only* way to pick identifiers out of modules.

I'm not proposing a better solution because I don't have one right now. But if it would help appease you that I suggest something: keep "import using", let go of "hiding", and reuse the "using" keyword to select symbols from specific modules to break ambiguities. (Names must always be module-qualified to break ambiguities with classes that have the same name as a module.)

Halfway there. Why is hiding such an issue? Explain to me how with only using imports you can recreate the "import String without String.UTF8View" example in the proposal.

···

import Foo
import Bar

using Foo.Baz; // in case of ambiguity, pick Foo.Baz
using extension Bar.Date; // in case of ambiguity on any Date extension method, pick the one in Bar
using extension Foo.Date.frob; // except for frob, that one comes from Foo

I know that this has problems on its own. Notably, `using` can't be a contextual keyword, the identifier now has to be reserved.

Félix

Le 20 juil. 2016 à 23:51:15, Robert Widmann <rwidmann@apple.com> a écrit :

On Jul 20, 2016, at 9:37 PM, Félix Cloutier via swift-evolution <swift-evolution@swift.org> wrote:

The problem is that by specifying "import Foo using (Baz)", I get nothing else from Foo. If I only want to exclude one conflicting name, I would have:

import Foo
import Bar hiding (Baz)

In case of a conflict, my "internal monologue" is more like "take Baz from Foo" than "don't take Baz from Bar".

How else would you resolve an ambiguity than by selecting the appropriate declaration and hiding the others? Swift’s semantic analysis cannot read your mind, and neither can your (or mine) proposal for renaming syntax - in that you still have to import both modules either way. You may as well be explicit about which name you’re actually using and which ones you’re actually hiding, eh?

Félix

Le 20 juil. 2016 à 20:46:18, Robert Widmann <devteam.codafi@gmail.com> a écrit :

~Robert Widmann

2016/07/20 20:07、Félix Cloutier via swift-evolution <swift-evolution@swift.org> のメッセージ:

My understanding is that we want "using" and "hiding" because we want the ability to either take just a few things OR leave out just a few things. With a unified "import Foo (A = B, C = D, E = _) syntax, we only get the ability to take a few things AND hide a few things.

Again, renaming is not something I want done with the same syntax as introducing and removing things from scope because they are distinct operations. This import tells me nothing from a semantic perspective and just seems easy to type rather than understand. Nowhere in the language can you find something that resembles this either.

I've never really been into a case where I badly had to *not* import a name, so while I see why it makes sense from a mathematical perspective to have "hiding", I'm not sure how much I'd miss it if it wasn't there.

I realize that it solves the ambiguous type problem, but I consider that it's a flawed solution. Instead of specifying from which module you want an import, you have to specify on which modules you don't want it.

You still specify which module you want to import from, so I don't see your point here. Given that Foo and Bar both define a class Baz, here's your import

import Foo using (Baz)
import Bar hiding (Baz)

What's the problem here? Isn't this exactly what you wanted to say in English (or whatever internal monologue you might have) spelled out in code? It scales immediately to multiple ambiguities and we can provide diagnostics to insert or remove identifiers in these lists to help the user out when they get stuck with an insufficiently inclusive or exclusive import list. The Python example is much more difficult to reason about from my perspective and from the perspective of the compiler. In fact, it's almost the code that's needed today to work around this problem - we're trying to fix the need for this here.

To see if we can get inspiration, I'd like to pitch an imperfect Python-like approach, where you could import a module as a namespace (and then you'd always have to write Module.Class, with modules systematically shadowing classes in the global namespace), or in addition to that, import every top-level name in the module into the file's global namespace. Names defined in multiple modules remain ambiguous unless explicitly shadowed:

private typealias OrderedSet = BTree.OrderedSet
private var foo: (Int) -> Int = Bar.foo

You would not be allowed to shadow a module with a class.

This, however, still does not solve the extension problem. Additionally, given that the default visibility for top-level names is internal, careless users could easily pollute the project's global namespace. Finally, for micro-frameworks that have a class with the same name as a module, you'd always have to write Name.Name, since the class can't shadow the module.

Félix

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Oops, hit Send too soon…

Pyry Jahkola wrote:

    import A // ok, unqualified import keeps A as a second-class identifier
    import B as B // ok, qualified import makes B a first-class identifier in file scope
    
    typealias A = Int // ok, shadows the module name A
    
    typealias B = Int
    // error: invalid redeclaration of 'Bar'
    // note: previously declared here: 'import Bar as Bar'

Of course that last error message was meant to read:

    // error: invalid redeclaration of 'B'
    // note: previously declared here: 'import B as B'

— Pyry

~Robert Widmann

2016/07/21 8:17、Félix Cloutier <felixcca@yahoo.ca <mailto:felixcca@yahoo.ca>> のメッセージ:

I know that the compiler can't read my mind. I don't mean to be frustrating.

If Foundation is imported in the bridging header of a project (as it is today, by default, in Swift framework projects), you get all of Foundation in all of your Swift code without a chance to write "import Foundation hiding (...)" anywhere. This reinforces my position that types should be selected affirmatively.

I don't understand this at all. The point of using and hiding is we create a file-local subset of identifiers you want to see. Bridging headers do not change that. I don't see how this is an example that affirmative is the *only* way to pick identifiers out of modules.

Here's a tiny Swift framework project: http://felixcloutier.com/tmp/SwiftImports.zip

The important part is that it has a Foo.h framework header, which #imports Cocoa. Through that, every Swift file in the project has access to everything that you get from <Cocoa/Cocoa.h>, as demonstrated by Foo.swift that creates a NSWindow without importing anything. Under the suggested model, I can't hide any of these symbols because the import does not happen on the Swift side, and there doesn't seem to be a way to break an ambiguity by hiding symbols in that case.

I probably misused "bridging header" because I don't fully understand how symbols flow between Objective-C and Swift in frameworks.

I'm not proposing a better solution because I don't have one right now. But if it would help appease you that I suggest something: keep "import using", let go of "hiding", and reuse the "using" keyword to select symbols from specific modules to break ambiguities. (Names must always be module-qualified to break ambiguities with classes that have the same name as a module.)

Halfway there. Why is hiding such an issue? Explain to me how with only using imports you can recreate the "import String without String.UTF8View" example in the proposal.

I'm pretty sure that I said that the example wasn't perfect. I wouldn't know how to do that. However, I don't know what else than breaking ambiguities you would want to use hiding for, and that example fully demonstrates how you would do that.

Félix

···

Le 21 juil. 2016 à 08:48:22, Robert Widmann <devteam.codafi@gmail.com> a écrit :

Regards
(From mobile)

~Robert Widmann

2016/07/21 0:13、Pyry Jahkola via swift-evolution <swift-evolution@swift.org> のメッセージ:

The problem is that by specifying "import Foo using (Baz)", I get nothing else from Foo. If I only want to exclude one conflicting name, I would have:

import Foo
import Bar hiding (Baz)

In case of a conflict, my "internal monologue" is more like "take Baz from Foo" than "don't take Baz from Bar".

How else would you resolve an ambiguity than by selecting the appropriate declaration and hiding the others? Swift’s semantic analysis cannot read your mind, and neither can your (or mine) proposal for renaming syntax - in that you still have to import both modules either way. You may as well be explicit about which name you’re actually using and which ones you’re actually hiding, eh?

Simple! You don't need to hide the others if we enforce a rule that explicitly mentioning one in the current file imports that name as a fileprivate identifier which shadows the ones not explicitly mentioned:

What you're describing is making a distinction between an open module and an imported module and is both additive and out of scope for this particular proposal. We didn't want to touch module re-exports until that proposal came up later. This is a fantastic idea that we have plans to incorporate in there nonetheless. This distinction is far more powerful than our current approach of just opening every module that gets imported into the top level namespace.

But it is a very limiting approach as has been seen in other languages and described here... designing a good system is as much about what it does today as what it will allow tomorrow and at what cost. Addressing the needs of today in a carpe diem fashion is an approach that works well when u are unsure if there will ever be a tomorrow, not for building the foundations of a legacy. To quote someone smarter "today's solutions are tomorrow's problems".

···

On Jul 21, 2016, at 9:20 AM, Robert Widmann via swift-evolution <swift-evolution@swift.org> wrote:

On 21 Jul 2016, at 09:51, Robert Widmann via swift-evolution <swift-evolution@swift.org> wrote:
On Jul 20, 2016, at 9:37 PM, Félix Cloutier via swift-evolution <swift-evolution@swift.org> wrote:

    import A using (X)
    import B // also exports X which gets shadowed by A.X
    import C // also exports X which gets shadowed by A.X

    assert(X.self == A.X.self)
    assert(X.self != B.X.self)
    assert(X.self != C.X.self)
    
    import D using (X)
    // error: invalid redeclaration of 'X'
    // note: previously declared here: 'import A using (X)'
    
    typealias X = Int
    // error: invalid redeclaration of 'X'
    // note: previously declared here: 'import A using (X)'

That would go nicely hand-in-hand with the idea that explicitly importing a module with a qualified name brings in the name of that module to the current file's scope:

    import A // ok, unqualified import keeps A as a second-class identifier
    import B as B // ok, qualified import makes B a first-class identifier in file scope
    
    typealias A = Int // ok, shadows the module name A
    
    typealias B = Int
    // error: invalid redeclaration of 'Bar'
    // note: previously declared here: 'import Bar as Bar'

Couldn't we find a synthesis with both the explicit qualification of modules and the explicit selection of imported names? I would strongly support that.

— Pyry

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

~Robert Widmann

2016/07/20 19:01、Xiaodi Wu via swift-evolution <
swift-evolution@swift.org> のメッセージ:

“The Phase Distinction” is a semantic one, not one built into the
import system itself.

I understand. To rephrase my question: why introduce this semantic
distinction to Swift?

What I meant is that even the system I’m modeling this on makes a

distinction between import directives that actually expose identifiers to
modules and import directives that modify identifiers that are already in
scope.

This is, IMO, very complex. I appreciate enormously the conceptual
simplicity of the current Swift approach which, for all of its
deficiencies, has only one import directive that does what it says on the
tin: it exposes identifiers. I'm not bothered if it gains the ability to
expose identifiers differently from one file to the next without keywords
firewalled from each other to preserve the notion of phases of import.

We are *not* changing the unqualified Swift import system. Take a
gander at the proposal again, or even the first draft. Swift has a
particularly strange syntax for qualified imports that hasn’t received
attention since it was first introduced 2 major versions ago. That thing
allows quite a variety of senseless variants that can be both completely
expressed by and subsumed by `using` and `hiding`.

My sense, which I think has been echoed by others, is that the proposed
solution is syntactically complex, and now that I understand that you're
thinking through a multi-phase concept, also conceptually multilayered. I'm
not arguing that the existing syntax for qualified imports doesn't need
changing, only that there is room for radical simplification of the
proposed solution IMO. As I re-read this proposal once more, it strikes me
that the motivating issues identified (needlessly specific, does not
compose, etc.) don't clearly argue for the specific direction proposed as
opposed to alternatives like Joe's.

Perhaps they need to reread the proposal. Syntactically complex how?
We're introducing two keywords and using tuple syntax. Our grammar changes
are laid bare and take up 7 lines. I think there might be a general air of
confusing semantics changes with syntax changes.

I can't speak for the general air, but putting on my disinterested reader
hat, I can see why the confusion might arise--

The Motivation section begins:
"The existing syntax for qualified imports..."

And the Proposed Solution begins:
"The grammar and semantics of qualified imports..."

But the Detailed Design begins:
"Qualified import syntax will be revised..."

It's neither here nor there in terms of the proposal content, but suffice
it to say that if one strings together the topic sentences in your
proposal, the overarching narrative to be gleaned here is: "The current
syntax for qualified imports is no good; therefore, we revise the semantics
of qualified imports by changing the syntax." Sure.

Ne’er the twain shall meet.

Yes, you explained this concept very clearly as it applies to Agda. But
I just don't see why we should care to have this distinction. Yet you are
very adamant about it. What am I missing?

We should care because that is precisely what the two operations *do*.
`using` and `hiding` introduce things or remove things from scope which is
a very different operation from taking something that is already in scope
and giving it a new name.

Perhaps I'm not phrasing my question very cogently. Of course, if we are
to have `using`, `hiding`, and `renaming`, we must observe the distinctions
between them.

If you don’t want to think of them as part of the same import process,

think of them instead in terms of their Swift equivalents today.

import Foundation using (Date) == import struct Foundation.Date
import Foundation hiding (Date) == import Foundation; @unavailable(*,
“…") typealias Date = Foundation.Date
import Foundation using (TimeInterval) renaming (TimeInterval, to:
Time) == import typealias Foundation.TimeInterval; typealias Time =
Foundation.TimeInterval

Notice how it takes two declarations to create a renaming? It is not
simple to drop being explicit about which names are actually in scope and
expect a renaming to just implicitly slip in a using declaration. Nor is
it simple to imagine _ as some magical namespace that you can pack away
unwanted definitions into. using and hiding are very physical things and
the rules for their behavior should be obvious and unambiguous - the
proposal contains some examples of valid and invalid declarations to help
with that.

The examples worry me, in fact. That we might need to contemplate the
behavior of a statement such as `import Foo using () hiding () hiding ()
using () hiding ()` suggests it's perhaps a little over-engineered for the
purpose. Why allow chaining of `using` and `hiding` anyway? The only
example given is of a nested type, which suggests nesting would be the way
to go:

We allow chaining specifically to avoid that nesting behavior. Let's
break up that chained import line by line to see why

import Swift using (String, Int, Double, Character)
                      hiding (String.UTF8View)

import Swift // Scope contains {all identifiers in Swift}
using (String, Int, Double, Character) // Scope contains {String.*,
Int.*, Double.*, Character.*}
                      hiding (String.UTF8View) // Scope contains
{{String.* - String.UTF8View.*}, Int.*, Double.*, Character.*}

We express the exact same example in a much more human-readable way.
You can read this out loud in plain English if you don't believe me:
"import everything in Swift that is in String, Int, Double, and Character
except String.UTF8View"

I have to disagree with your reasoning here. Unless I'm mistaken, the
readability we're most concerned with is that of the written text, not its
spoken form.

Nesting is an almost exclusively _visual_ way of organizing text and it
adds real clarity on the page. Of course, if your litmus test for
readability is literally trying to read it out loud, you would conclude
that nesting is inferior. However, you'd make that same conclusion about so
many other choices in Swift syntax (take, for instance, `:` instead of
`extends` or `->` instead of `returns`). I conclude, on the other hand,
that Swift is clearly not aiming to be AppleScript-like in this respect.

I disagree. Standard Library Swift reads left to right in a very
carefully chosen and consistent manner - it has been refined on this list
and by the team quite a lot to get it that way. You seem to think I’m
arguing verbosity is the end goal (AppleScript?): I’m not. Clarity is the
goal. It is unambiguous what you mean when you say

import Swift using (String, Int, Double) hiding (String.UTF8View)

Is it ambiguous to say `import Swift using (String hiding (UTF8View), Int,
Double)`?

What happens when it is time to extend this proposal to members?

Short answer: don't.

Long answer: I've been studying documentation for the Agda model on which
you're basing this proposal. I like it a lot, and it doesn't have any of
these issues we're discussing.

First: there's the distinction in Agda between `import` and `open`. It's
`open` that allows you to hide and "re-"name, and these operations only
concern the short, unqualified names that are exposed, never changing
what's imported. Thus, none of this is mutating the API. By contrast, I
see Agda offers `as` for importing something as something else, just like
what's has been suggested in this thread and the last. All of this--the
distinction between import and open, the distinction between `import ...
as` and what's essentially typealiasing--reflects very closely the
semantics found in other languages which I was trying to describe to you
(poorly) yesterday.

I don't see any facility in Agda where qualified names for definitions can
be vaporized post-hoc by `hiding`. [Yes, I know that technically you're not
making anything disappear, etc.; but the point is that when I'm hiding
frobnicate() from Foo the original Foo.frobnicate() is no longer in the way
of whatever I want to do.] Can you give an example where this is permitted
in any other language? Why would you want to be able to do this to
individual members? As discussed in the previous thread, it seems a recipe
for some really nasty stuff.

Second: Agda explicitly prohibits chaining `using` and `hiding`. In fact,
one piece of documentation said that the reason for this prohibition was
"obvious."

If we're to go down the road of taking inspiration from Agda, I think your
proposal is missing some of the key things that make it work well. In that
language, a sharp distinction is drawn between import and open, and we
could do well to observe the same. I've always thought it problematic that
`import Foundation` in Swift does both so that we're stuck with unqualified
names for everything in Foundation. It would probably be too late to make
that distinction past Swift 3. Moreover, I'd seriously reconsider for the
future whether facilities to hide members are appropriate for import
statements, and whether trying to accommodate such facilities now is
worthwhile. And I'd consider why it is that Agda prohibits freeform
chaining of using, hiding and renaming, and whether Swift needs it for
anything. It is not ideal to have to explain what `hiding (String) using
(String)` does; it shouldn't be possible to write such a thing.

This syntax fits well with the overall direction of the language itself.

For example, I use whitespace here out of habit but here’s nothing stopping
you from making these look like the (meta)function invocations they really
are

import Swift
  using(String, Int, Double)
  hiding(String.UTF8View)

In the comments you've used the nested notation `{{String.* -
String.UTF8View.*}, Int.*, Double.*, Character.*}` to explain what it is
your proposed syntax means. I really do find that, with all the punctuation
in it, clearer than the chained notation. Of course, written `(String
hiding UTF8View), Int, Double, Character`, it would be clearer still.

Critically, it eliminates the possibility of absurd chains of `hiding ()
using () hiding () using ()`, which create many more ways to represent the
same subset of imports than can a nested syntax.

If you wish to express this, why should the language stop you?

Given two options for syntax, one where expressing this chain is never
necessary or even possible, and another where it is possible and perhaps
even necessary, the former wins in terms of clarity, no?

Yours does not exclude this possibility, it merely shuffles it around
(unless you plan on banning the empty tuple. In which case, why?)

The first part answers the second :)
I wouldn't use tuples at all. Moreover, since you seem to be requiring
parentheses around single elements, you're not even really using tuples
here, but rather argument lists. As Brandon mentioned above, I don't see
the need to try to use either tuples or argument lists:

import Swift using String, Int, Double
import Swift using (String hiding UTF8View), Int, Double

import Swift using (String using(UTF8View hiding ()), Int hiding (),
Double hiding())

At this point we’re just circling around an edge case described in the
proposal as ripe for a merge-by-diagnostic.

The point I'm making is that chaining is inelegant in general, the edge
case being only an extreme example of where it goes off the rails. In other
words, the edge case is an argument ad absurdum for a larger issue. Of
course, the edge case itself can be flagged by a diagnostic. There
shouldn't have to be a need for it.

Or better yet: we can offer diagnostics about this case just as we can

···

On Thu, Jul 21, 2016 at 2:02 AM, Robert Widmann <rwidmann@apple.com> wrote:

On Jul 20, 2016, at 11:56 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Thu, Jul 21, 2016 at 1:40 AM, Robert Widmann <rwidmann@apple.com> > wrote:

On Jul 20, 2016, at 10:04 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Wed, Jul 20, 2016 at 10:33 PM, Robert Widmann < >> devteam.codafi@gmail.com> wrote:

On Wed, Jul 20, 2016 at 8:10 PM, Robert Widmann <rwidmann@apple.com> >>> wrote:

On Jul 20, 2016, at 5:47 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Wed, Jul 20, 2016 at 6:30 PM, Robert Widmann <rwidmann@apple.com> >>>> wrote:

offer diagnostics for doubled imports or improperly nested imports. A big
part of why this proposal exists is because the existing diagnostics around
qualified imports are uninformative and unhelpful.

Now reread the chaining example in the proposal.

import Swift using (String hiding (UTF8View), Int, Double)

A qualified import is defining a procedure to import a subset of

identifiers. That’s it.

Right, and I think an entirely different way of thinking about this
would be much easier to learn and teach. Whether using, hiding, and
renaming are to be supported now, later, or never, my mental picture of how
it fits together is quite simple:

Analogy--suppose I am a pickle merchant. I import Foo-branded pickles
from vendor X. I must re-label them with the right nutritional information
before I can sell in this country. I can have labels printed saying that
they are Foo-branded pickles. I can have them branded as Bar-branded
pickles. Or I can have the labels deliberately misprinted, and then these
pickles will never see the light of day. Point is, each of these is an
active choice; even if I sell these as Bar-branded pickles, it's not that
these pickles reached the domestic market as Foo-branded pickles, after
which I scratched out the label with a Sharpie. These pickles had no
domestic brand until I gave it one.

Back to importing modifiers--I import type Foo from module X. In my
code, I need to make a choice to call this type Foo, or Bar, or nothing at
all. In other words, there is only one directive, importing, and I am
importing `Foo as Foo`, `Foo as Bar`, or `Foo as _`. Meanwhile, `import X
using Foo` or `import X.Foo` (whatever the color of the bikeshed) would
just be a shorthand for `import X using Foo as Foo` or `import X.Foo as
Foo`. In this conceptualization, if I choose to import Foo as Bar, it's not
that I'm importing Foo into the scope, then changing the identifier to Bar.
The only identifier it ever has in this scope is Bar.

And I’m the one with the complex semantics? :)

I'm just trying to put into words what I'm familiar with after working
in other languages such as Python.

Python may not be the right mindset for this. Their import story is
much simpler because of their module system and generally simpler
programming model.

How about this:

Using and Hiding relate to each other the way && and || do for bools.
If && can be said to “prefer to return false, but return true given no
other alternative” and || can be said to “prefer returning true, but return
false given no other alternative”, then hiding can be said to “prefer
importing all identifiers unless told not to in specific instances” and
using can be said to “prefer importing no identifiers unless told to in
specific instances”.

import Module.Name using (A, B, C, …) === import Module.Name hiding
(ALL_NAMES - {A, B, C, ...})
import Module.Name hiding (A, B, C, …) === import Module.Name using
(ALL_NAMES - {A, B, C, ...})

That seems a particularly simple explanation to me. Let me know if
anything else is unclear.

Your mental framework is clear. It's one that's just not found in very
many other languages. Many of these have import declarations (or similar)
with simpler syntax, yet they seem to address at least some of the problems
that motivate your proposal. I guess my question in the end is, why have
you chosen Agda as the basis for qualified imports in Swift and not one of
these other languages?

I chose Agda because it's the only major language I could find that
treated identifiers like notation and used its module system for *real* organization
of code; it's just a name, it can change if you want it to. The entire
language is flexible. You can redefine functions with just an =, you can
give new syntactic transformations without having to write crazy macros or
worrying about hygiene. You get so much support from the type system too
that all of these features just work together and you can sit back and feel
satisfied that your tools pushed you to write *good code. *I wrote it
with Agda in mind because they took the time to think about the
interactions between modules, code, and scope in a way we just haven't had
the time to yet.

Many of these imports have simpler syntax but don't chain so lose
expressiveness (Java). Or may have simpler surface syntax but don't
properly interact with a moduleless system (Python, Java). Or may have a
simpler syntax and give way to ambiguities (Python) when used in Swift.
Agda is unambiguous, scalable, extensible, and simple.

Please don't confuse what's new with what's complex. But at the same
time if there are any unexplainable loopholes we should know about them. I
just haven't heard much I couldn't point to the proposal about yet so I
don't see a reason to change.

Thanks for this insight. It's clear that you're aiming at a far richer
system in the future. However, it's hard to gauge how far Swift will
eventually scale/extend what you're proposing here. I don't think there's
any denying that the solution you've chosen introduces more involved
semantics and a more verbose syntax than what's strictly necessary to
address the specific motivating problem you give in this particular
instance. Without a better sense of the outer bounds of how far this system
will eventually be pushed, it's hard to judge whether the eventual payoff
will be "worth it." And without any mention of the grander plan, I suspect
the feedback you're going to get will continue along the path of trying to
tear away whatever you're trying to put in place for the future which is
not discernibly necessary for solving the immediate problem at hand.

If you believe this proposal is not extensible then cite a particular
example, otherwise I can only say that I can’t tell the future. The syntax
presented here is inspired by a tiny, tiny fragment of a language with an
incredibly rich import mechanism and module system. It’s unclear how much
of that system fits with Swift current - we already tried to formalize
quite a bit in the first draft. For right now, *this* proposal is
trying to clean up a long-neglected part of the language. If you feel that
we’ve failed in that regard then tell me. Otherwise I’m fine with my
syntax being pulled in a different direction in future additive proposals.
I just want to fix this part of the language before the window on
source-breaking changes closes for a while.

On Jul 20, 2016, at 4:17 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Wed, Jul 20, 2016 at 4:57 PM, Robert Widmann <rwidmann@apple.com> >>>>> wrote:

On Jul 20, 2016, at 2:52 PM, Robert Widmann via swift-evolution < >>>>>> swift-evolution@swift.org> wrote:

On Jul 20, 2016, at 2:35 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Wed, Jul 20, 2016 at 4:24 PM, Robert Widmann <rwidmann@apple.com> >>>>>> wrote:

On Jul 20, 2016, at 2:19 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Wed, Jul 20, 2016 at 4:06 PM, Robert Widmann <rwidmann@apple.com> >>>>>>> wrote:

On Jul 20, 2016, at 2:04 PM, Robert Widmann via swift-evolution < >>>>>>>> swift-evolution@swift.org> wrote:

On Jul 20, 2016, at 1:59 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Why is hiding in-scope but renaming out-of-scope?

Because hiding and renaming can be used in combination to subset
out APIs, not alter them.

I mistyped. Should be "Because hiding and using can be used in
combination to subset out APIs, not alter them."

Sure, I buy that.

Both are additive to Swift,

As part of this proposal, both are source-breaking.

I don't see how. If hiding were cut from the proposal, adding it
later with even the exact syntax you propose should break no pre-existing
code--am I wrong?

Renaming the way we originally laid it out would certainly be
additive. The way you have it laid out would overlap a bit with hiding,
sure, but it is still additive and (IMO, but I’m probably among a tiny
minority of users that has used a proof assistant’s syntax as the basis for
a proposal!) a good thing to have.

Sorry, I fear I've incorrectly communicated the point I was trying to
make. I'm not advocating here for inclusion of renaming as part of this
proposal. I simply think that--even though I buy your claim that hiding and
using both subset out APIs--hiding has more affinity with renaming and the
two facilities probably ought to be considered together, whenever that is.

Thus, I'm suggesting that it would be feasible to postpone discussion
of hiding until such future time as a fully fleshed out renaming scheme is
proposed. A revamped source-breaking import syntax without either hiding or
renaming could be put in place now, and future addition of hiding and/or
renaming would not have to be source-breaking. Is there something wrong
with this argument?

There is still a useful to distinction to be made between explicitly
renaming an API and explicitly hiding an API. Scala’s syntax to rename to
underbar is a convenient notation for that kind of thing, but it goes
against making qualified imports explicit and it means that renaming
necessarily has to import identifiers into scope as well as rename them.
What the OP (maybe it was you, sorry if it was) meant by “equivalent”
missed the point that

import Swift hiding (String)

doesn’t translate into

import Swift renaming (String, to: _)

it translates into

import Swift hiding () renaming (String, to: _)

Renaming introducing identifiers into scope seems like a phase-shift
and is not something the verb “rename” implies should happen here. It’s an
interesting little hole in Agda’s module system that you can use

open A hiding (xs) renaming (ys to zs)

to mean

open A using (A; xs; ys) renaming (ys to zs)

Actually, scratch that. Their documentation explicitly mentions that
hiding and renaming may not be mixed because of the phase distinction and
recommend the using translation above as the way to go.

This is very illuminating. I think I've rather misunderstood what it
is you're proposing. I wonder if others did also.

The syntax you proposed seemed cumbersome to me because my mental
model of importing (informed by my probably superficial understanding of
vanilla procedural programming languages) has only one phase: importing.
This is why I proposed radically simplifying the spelling. To me, all of
these operations are just sugar on a single import phase, where "stuff"
from outside the module is "brought into" the module, either with the same
name ("using"), a different name ("renaming"), or no name ("hiding").

But what you're saying here--if I understand correctly--is that you're
proposing a multi-phase import system, where the possible phases, which can
be composed in varying orders, are "using", "hiding", and "renaming". This
is much, much more elaborate than I had contemplated. So beyond the
bikeshedding of syntax, I'd ask: why do we need this multi-phase model of
importing?

and as has been argued by others, the former is a special case of

the latter.

A special case that cannot cause large-scale file-relative changes
to APIs. Renaming is primarily used in other languages that treat free
functions as more canonical than we do, or allow operator definitions that
can be used as notation.

I don't know about 'primary use,' but the most common use I've
experienced in Python, for example, is the mundane task of importing module
Foo2 as Foo.

And I still want that kind of syntax. I just want to get the
breaking changes out of the way to make room for it in the future.

Right. See above about my argument as to which parts of your proposal
have to be source-breaking, and which don't.

In those cases, you often have your own notation you’d like to use.

In Swift, such changes should be rare enough that if you can’t solve them
with a disambiguating qualified import then you can just redeclare the
identifier some other way (typealias, top-level let, wrapper class,
whatever).

You've already stripped out renaming of members from the proposal. I
agree wholeheartedly. The only flavor of renaming I'm thinking of here is
equivalent to a fileprivate typealias and hiding, which cannot be done in
this version of the proposal because hiding always comes before
typealiasing and you can't typealias what isn't imported. It isn't about
altering APIs any more than a fileprivate typealias can be thought of as
altering APIs.

In the sense that you can’t use the original identifier if you
rename it, it is an alteration. John brought up a great point about
exporting these things and how it could be a potentially dangerous thing.
Even used locally, there’s the potential for people to specify 500 lines of
import renaming crap that has to be copypasta’d throughout the codebase to
maintain that particular style - not a use-case I’ve ever seen, but the
potential is there.

This is, I think, a spurious argument. I can equally have 500 lines
of private typealiased crap that has to be copypasta'd.

On Wed, Jul 20, 2016 at 15:55 Brandon Knope <bknope@me.com> wrote:

I meant is there any reason for requiring parentheses

On Jul 20, 2016, at 4:53 PM, Robert Widmann <rwidmann@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@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@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@me.com> wrote:

On Jul 20, 2016, at 3:08 PM, Xiaodi Wu via swift-evolution < >>>>>>>>>> swift-evolution@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@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
<https://github.com/apple/swift-evolution/pull/440&gt;, or as a
gist
<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6&gt;
.

Thanks,

~Robert Widmann

Qualified Imports Revisited

   - Proposal: SE-NNNN
   <https://gist.github.com/CodaFi/NNNN-first-class-qualified-imports.md&gt;
   - Authors: Robert Widmann <https://github.com/codafi&gt;, TJ
   Usiyan <https://github.com/griotspeak&gt;
   - Status: Awaiting review
   - Review manager: TBD

<qualified-imports.md · GitHub;
Introduction

We propose a complete overhaul of the qualified imports syntax
and semantics.

<qualified-imports.md · GitHub;
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.

<qualified-imports.md · GitHub
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.

<qualified-imports.md · GitHub
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.DateComponentsimport 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: "...") // Validvar 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.UTF8Viewimport 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.

<qualified-imports.md · GitHub
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.

<qualified-imports.md · GitHub
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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Cool, thanks for the feedback. Now, is there a reason to consider any of this before Swift 3's timeframe? Isn't this all additive?

In any case then, I'll hold that proposal back until Swift 3's scope has been fixed.

— Pyry

···

On 21 Jul 2016, at 10:20, Robert Widmann <devteam.codafi@gmail.com> wrote:

Simple! You don't need to hide the others if we enforce a rule that explicitly mentioning one in the current file imports that name as a fileprivate identifier which shadows the ones not explicitly mentioned:

What you're describing is making a distinction between an open module and an imported module and is both additive and out of scope for this particular proposal. We didn't want to touch module re-exports until that proposal came up later. This is a fantastic idea that we have plans to incorporate in there nonetheless. This distinction is far more powerful than our current approach of just opening every module that gets imported into the top level namespace.

This proposal is specifically source breaking because we're only trying to deprecate and remove

import {class|func|struct|...} Module.Entity

-style imports (which not too many people seem to know actually exist).

~Robert Widmann

2016/07/21 0:27、Pyry Jahkola <pyry.jahkola@iki.fi> のメッセージ:

···

On 21 Jul 2016, at 10:20, Robert Widmann <devteam.codafi@gmail.com> wrote:

Simple! You don't need to hide the others if we enforce a rule that explicitly mentioning one in the current file imports that name as a fileprivate identifier which shadows the ones not explicitly mentioned:

What you're describing is making a distinction between an open module and an imported module and is both additive and out of scope for this particular proposal. We didn't want to touch module re-exports until that proposal came up later. This is a fantastic idea that we have plans to incorporate in there nonetheless. This distinction is far more powerful than our current approach of just opening every module that gets imported into the top level namespace.

Cool, thanks for the feedback. Now, is there a reason to consider any of this before Swift 3's timeframe? Isn't this all additive?

In any case then, I'll hold that proposal back until Swift 3's scope has been fixed.

— Pyry

~Robert Widmann

2016/07/21 8:23、Xiaodi Wu <xiaodi.wu@gmail.com> のメッセージ:

~Robert Widmann

2016/07/20 19:01、Xiaodi Wu via swift-evolution <swift-evolution@swift.org> のメッセージ:

“The Phase Distinction” is a semantic one, not one built into the import system itself.

I understand. To rephrase my question: why introduce this semantic distinction to Swift?

What I meant is that even the system I’m modeling this on makes a distinction between import directives that actually expose identifiers to modules and import directives that modify identifiers that are already in scope.

This is, IMO, very complex. I appreciate enormously the conceptual simplicity of the current Swift approach which, for all of its deficiencies, has only one import directive that does what it says on the tin: it exposes identifiers. I'm not bothered if it gains the ability to expose identifiers differently from one file to the next without keywords firewalled from each other to preserve the notion of phases of import.

We are not changing the unqualified Swift import system. Take a gander at the proposal again, or even the first draft. Swift has a particularly strange syntax for qualified imports that hasn’t received attention since it was first introduced 2 major versions ago. That thing allows quite a variety of senseless variants that can be both completely expressed by and subsumed by `using` and `hiding`.

My sense, which I think has been echoed by others, is that the proposed solution is syntactically complex, and now that I understand that you're thinking through a multi-phase concept, also conceptually multilayered. I'm not arguing that the existing syntax for qualified imports doesn't need changing, only that there is room for radical simplification of the proposed solution IMO. As I re-read this proposal once more, it strikes me that the motivating issues identified (needlessly specific, does not compose, etc.) don't clearly argue for the specific direction proposed as opposed to alternatives like Joe's.

Perhaps they need to reread the proposal. Syntactically complex how? We're introducing two keywords and using tuple syntax. Our grammar changes are laid bare and take up 7 lines. I think there might be a general air of confusing semantics changes with syntax changes.

I can't speak for the general air, but putting on my disinterested reader hat, I can see why the confusion might arise--

The Motivation section begins:
"The existing syntax for qualified imports..."

And the Proposed Solution begins:
"The grammar and semantics of qualified imports..."

But the Detailed Design begins:
"Qualified import syntax will be revised..."

It's neither here nor there in terms of the proposal content, but suffice it to say that if one strings together the topic sentences in your proposal, the overarching narrative to be gleaned here is: "The current syntax for qualified imports is no good; therefore, we revise the semantics of qualified imports by changing the syntax." Sure.

Ne’er the twain shall meet.

Yes, you explained this concept very clearly as it applies to Agda. But I just don't see why we should care to have this distinction. Yet you are very adamant about it. What am I missing?

We should care because that is precisely what the two operations do. `using` and `hiding` introduce things or remove things from scope which is a very different operation from taking something that is already in scope and giving it a new name.

Perhaps I'm not phrasing my question very cogently. Of course, if we are to have `using`, `hiding`, and `renaming`, we must observe the distinctions between them.

If you don’t want to think of them as part of the same import process, think of them instead in terms of their Swift equivalents today.

import Foundation using (Date) == import struct Foundation.Date
import Foundation hiding (Date) == import Foundation; @unavailable(*, “…") typealias Date = Foundation.Date
import Foundation using (TimeInterval) renaming (TimeInterval, to: Time) == import typealias Foundation.TimeInterval; typealias Time = Foundation.TimeInterval

Notice how it takes two declarations to create a renaming? It is not simple to drop being explicit about which names are actually in scope and expect a renaming to just implicitly slip in a using declaration. Nor is it simple to imagine _ as some magical namespace that you can pack away unwanted definitions into. using and hiding are very physical things and the rules for their behavior should be obvious and unambiguous - the proposal contains some examples of valid and invalid declarations to help with that.

The examples worry me, in fact. That we might need to contemplate the behavior of a statement such as `import Foo using () hiding () hiding () using () hiding ()` suggests it's perhaps a little over-engineered for the purpose. Why allow chaining of `using` and `hiding` anyway? The only example given is of a nested type, which suggests nesting would be the way to go:

We allow chaining specifically to avoid that nesting behavior. Let's break up that chained import line by line to see why

import Swift using (String, Int, Double, Character)
                      hiding (String.UTF8View)

import Swift // Scope contains {all identifiers in Swift}
using (String, Int, Double, Character) // Scope contains {String.*, Int.*, Double.*, Character.*}
                      hiding (String.UTF8View) // Scope contains {{String.* - String.UTF8View.*}, Int.*, Double.*, Character.*}

We express the exact same example in a much more human-readable way. You can read this out loud in plain English if you don't believe me: "import everything in Swift that is in String, Int, Double, and Character except String.UTF8View"

I have to disagree with your reasoning here. Unless I'm mistaken, the readability we're most concerned with is that of the written text, not its spoken form.

Nesting is an almost exclusively _visual_ way of organizing text and it adds real clarity on the page. Of course, if your litmus test for readability is literally trying to read it out loud, you would conclude that nesting is inferior. However, you'd make that same conclusion about so many other choices in Swift syntax (take, for instance, `:` instead of `extends` or `->` instead of `returns`). I conclude, on the other hand, that Swift is clearly not aiming to be AppleScript-like in this respect.

I disagree. Standard Library Swift reads left to right in a very carefully chosen and consistent manner - it has been refined on this list and by the team quite a lot to get it that way. You seem to think I’m arguing verbosity is the end goal (AppleScript?): I’m not. Clarity is the goal. It is unambiguous what you mean when you say

import Swift using (String, Int, Double) hiding (String.UTF8View)

Is it ambiguous to say `import Swift using (String hiding (UTF8View), Int, Double)`?

What happens when it is time to extend this proposal to members?

Short answer: don't.

Long answer: I've been studying documentation for the Agda model on which you're basing this proposal. I like it a lot, and it doesn't have any of these issues we're discussing.

First: there's the distinction in Agda between `import` and `open`. It's `open` that allows you to hide and "re-"name, and these operations only concern the short, unqualified names that are exposed, never changing what's imported. Thus, none of this is mutating the API. By contrast, I see Agda offers `as` for importing something as something else, just like what's has been suggested in this thread and the last. All of this--the distinction between import and open, the distinction between `import ... as` and what's essentially typealiasing--reflects very closely the semantics found in other languages which I was trying to describe to you (poorly) yesterday.

I don't see any facility in Agda where qualified names for definitions can be vaporized post-hoc by `hiding`. [Yes, I know that technically you're not making anything disappear, etc.; but the point is that when I'm hiding frobnicate() from Foo the original Foo.frobnicate() is no longer in the way of whatever I want to do.] Can you give an example where this is permitted in any other language? Why would you want to be able to do this to individual members? As discussed in the previous thread, it seems a recipe for some really nasty stuff.

That was renaming. That is out of scope for this proposal.

Second: Agda explicitly prohibits chaining `using` and `hiding`. In fact, one piece of documentation said that the reason for this prohibition was "obvious."

Agda doesn't permit using and hiding to be chained because they have a proper module system that encourages hiding by nesting modules to any depth and qualified importing or hiding their contents. We have no such facility, and I don't anticipate its inclusion in Swift, so I recovered that same power here.

If we're to go down the road of taking inspiration from Agda, I think your proposal is missing some of the key things that make it work well. In that language, a sharp distinction is drawn between import and open, and we could do well to observe the same. I've always thought it problematic that `import Foundation` in Swift does both so that we're stuck with unqualified names for everything in Foundation. It would probably be too late to make that distinction past Swift 3.

That is a function of a module system and relevant to the second proposal. As qualified imports today make no distinction between open and imported modules, we felt it wasn't necessary to include it here. We want this. We want it badly. But this isn't the right place to ask for it.

Moreover, I'd seriously reconsider for the future whether facilities to hide members are appropriate for import statements, and whether trying to accommodate such facilities now is worthwhile. And I'd consider why it is that Agda prohibits freeform chaining of using, hiding and renaming, and whether Swift needs it for anything. It is not ideal to have to explain what `hiding (String) using (String)` does; it shouldn't be possible to write such a thing.

Then, as I've said before, we'll use diagnostics to make this case better. This is a case that is explicitly spelled out in the proposal. If it would remove this case, you could argue that directives may not refer to unqualified identifiers from earlier directives. That seems unnecessarily artificial just to remove something that has completely valid semantics.

···

On Thu, Jul 21, 2016 at 2:02 AM, Robert Widmann <rwidmann@apple.com> wrote:

On Jul 20, 2016, at 11:56 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Thu, Jul 21, 2016 at 1:40 AM, Robert Widmann <rwidmann@apple.com> wrote:

On Jul 20, 2016, at 10:04 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Wed, Jul 20, 2016 at 10:33 PM, Robert Widmann <devteam.codafi@gmail.com> wrote:

On Wed, Jul 20, 2016 at 8:10 PM, Robert Widmann <rwidmann@apple.com> wrote:

On Jul 20, 2016, at 5:47 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Wed, Jul 20, 2016 at 6:30 PM, Robert Widmann <rwidmann@apple.com> wrote:

This syntax fits well with the overall direction of the language itself. For example, I use whitespace here out of habit but here’s nothing stopping you from making these look like the (meta)function invocations they really are

import Swift
  using(String, Int, Double)
  hiding(String.UTF8View)

In the comments you've used the nested notation `{{String.* - String.UTF8View.*}, Int.*, Double.*, Character.*}` to explain what it is your proposed syntax means. I really do find that, with all the punctuation in it, clearer than the chained notation. Of course, written `(String hiding UTF8View), Int, Double, Character`, it would be clearer still.

Critically, it eliminates the possibility of absurd chains of `hiding () using () hiding () using ()`, which create many more ways to represent the same subset of imports than can a nested syntax.

If you wish to express this, why should the language stop you?

Given two options for syntax, one where expressing this chain is never necessary or even possible, and another where it is possible and perhaps even necessary, the former wins in terms of clarity, no?

Yours does not exclude this possibility, it merely shuffles it around (unless you plan on banning the empty tuple. In which case, why?)

The first part answers the second :)
I wouldn't use tuples at all. Moreover, since you seem to be requiring parentheses around single elements, you're not even really using tuples here, but rather argument lists. As Brandon mentioned above, I don't see the need to try to use either tuples or argument lists:

import Swift using String, Int, Double
import Swift using (String hiding UTF8View), Int, Double

import Swift using (String using(UTF8View hiding ()), Int hiding (), Double hiding())

At this point we’re just circling around an edge case described in the proposal as ripe for a merge-by-diagnostic.

The point I'm making is that chaining is inelegant in general, the edge case being only an extreme example of where it goes off the rails. In other words, the edge case is an argument ad absurdum for a larger issue. Of course, the edge case itself can be flagged by a diagnostic. There shouldn't have to be a need for it.

Or better yet: we can offer diagnostics about this case just as we can offer diagnostics for doubled imports or improperly nested imports. A big part of why this proposal exists is because the existing diagnostics around qualified imports are uninformative and unhelpful.

Now reread the chaining example in the proposal.

import Swift using (String hiding (UTF8View), Int, Double)

A qualified import is defining a procedure to import a subset of identifiers. That’s it.

Right, and I think an entirely different way of thinking about this would be much easier to learn and teach. Whether using, hiding, and renaming are to be supported now, later, or never, my mental picture of how it fits together is quite simple:

Analogy--suppose I am a pickle merchant. I import Foo-branded pickles from vendor X. I must re-label them with the right nutritional information before I can sell in this country. I can have labels printed saying that they are Foo-branded pickles. I can have them branded as Bar-branded pickles. Or I can have the labels deliberately misprinted, and then these pickles will never see the light of day. Point is, each of these is an active choice; even if I sell these as Bar-branded pickles, it's not that these pickles reached the domestic market as Foo-branded pickles, after which I scratched out the label with a Sharpie. These pickles had no domestic brand until I gave it one.

Back to importing modifiers--I import type Foo from module X. In my code, I need to make a choice to call this type Foo, or Bar, or nothing at all. In other words, there is only one directive, importing, and I am importing `Foo as Foo`, `Foo as Bar`, or `Foo as _`. Meanwhile, `import X using Foo` or `import X.Foo` (whatever the color of the bikeshed) would just be a shorthand for `import X using Foo as Foo` or `import X.Foo as Foo`. In this conceptualization, if I choose to import Foo as Bar, it's not that I'm importing Foo into the scope, then changing the identifier to Bar. The only identifier it ever has in this scope is Bar.

And I’m the one with the complex semantics? :)

I'm just trying to put into words what I'm familiar with after working in other languages such as Python.

Python may not be the right mindset for this. Their import story is much simpler because of their module system and generally simpler programming model.

How about this:

Using and Hiding relate to each other the way && and || do for bools. If && can be said to “prefer to return false, but return true given no other alternative” and || can be said to “prefer returning true, but return false given no other alternative”, then hiding can be said to “prefer importing all identifiers unless told not to in specific instances” and using can be said to “prefer importing no identifiers unless told to in specific instances”.

import Module.Name using (A, B, C, …) === import Module.Name hiding (ALL_NAMES - {A, B, C, ...})
import Module.Name hiding (A, B, C, …) === import Module.Name using (ALL_NAMES - {A, B, C, ...})

That seems a particularly simple explanation to me. Let me know if anything else is unclear.

Your mental framework is clear. It's one that's just not found in very many other languages. Many of these have import declarations (or similar) with simpler syntax, yet they seem to address at least some of the problems that motivate your proposal. I guess my question in the end is, why have you chosen Agda as the basis for qualified imports in Swift and not one of these other languages?

I chose Agda because it's the only major language I could find that treated identifiers like notation and used its module system for real organization of code; it's just a name, it can change if you want it to. The entire language is flexible. You can redefine functions with just an =, you can give new syntactic transformations without having to write crazy macros or worrying about hygiene. You get so much support from the type system too that all of these features just work together and you can sit back and feel satisfied that your tools pushed you to write good code. I wrote it with Agda in mind because they took the time to think about the interactions between modules, code, and scope in a way we just haven't had the time to yet.

Many of these imports have simpler syntax but don't chain so lose expressiveness (Java). Or may have simpler surface syntax but don't properly interact with a moduleless system (Python, Java). Or may have a simpler syntax and give way to ambiguities (Python) when used in Swift. Agda is unambiguous, scalable, extensible, and simple.

Please don't confuse what's new with what's complex. But at the same time if there are any unexplainable loopholes we should know about them. I just haven't heard much I couldn't point to the proposal about yet so I don't see a reason to change.

Thanks for this insight. It's clear that you're aiming at a far richer system in the future. However, it's hard to gauge how far Swift will eventually scale/extend what you're proposing here. I don't think there's any denying that the solution you've chosen introduces more involved semantics and a more verbose syntax than what's strictly necessary to address the specific motivating problem you give in this particular instance. Without a better sense of the outer bounds of how far this system will eventually be pushed, it's hard to judge whether the eventual payoff will be "worth it." And without any mention of the grander plan, I suspect the feedback you're going to get will continue along the path of trying to tear away whatever you're trying to put in place for the future which is not discernibly necessary for solving the immediate problem at hand.

If you believe this proposal is not extensible then cite a particular example, otherwise I can only say that I can’t tell the future. The syntax presented here is inspired by a tiny, tiny fragment of a language with an incredibly rich import mechanism and module system. It’s unclear how much of that system fits with Swift current - we already tried to formalize quite a bit in the first draft. For right now, this proposal is trying to clean up a long-neglected part of the language. If you feel that we’ve failed in that regard then tell me. Otherwise I’m fine with my syntax being pulled in a different direction in future additive proposals. I just want to fix this part of the language before the window on source-breaking changes closes for a while.

On Jul 20, 2016, at 4:17 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Wed, Jul 20, 2016 at 4:57 PM, Robert Widmann <rwidmann@apple.com> wrote:

On Jul 20, 2016, at 2:52 PM, Robert Widmann via swift-evolution <swift-evolution@swift.org> wrote:

On Jul 20, 2016, at 2:35 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Wed, Jul 20, 2016 at 4:24 PM, Robert Widmann <rwidmann@apple.com> wrote:

On Jul 20, 2016, at 2:19 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Wed, Jul 20, 2016 at 4:06 PM, Robert Widmann <rwidmann@apple.com> wrote:

On Jul 20, 2016, at 2:04 PM, Robert Widmann via swift-evolution <swift-evolution@swift.org> wrote:

On Jul 20, 2016, at 1:59 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Why is hiding in-scope but renaming out-of-scope?

Because hiding and renaming can be used in combination to subset out APIs, not alter them.

I mistyped. Should be "Because hiding and using can be used in combination to subset out APIs, not alter them."

Sure, I buy that.

Both are additive to Swift,

As part of this proposal, both are source-breaking.

I don't see how. If hiding were cut from the proposal, adding it later with even the exact syntax you propose should break no pre-existing code--am I wrong?

Renaming the way we originally laid it out would certainly be additive. The way you have it laid out would overlap a bit with hiding, sure, but it is still additive and (IMO, but I’m probably among a tiny minority of users that has used a proof assistant’s syntax as the basis for a proposal!) a good thing to have.

Sorry, I fear I've incorrectly communicated the point I was trying to make. I'm not advocating here for inclusion of renaming as part of this proposal. I simply think that--even though I buy your claim that hiding and using both subset out APIs--hiding has more affinity with renaming and the two facilities probably ought to be considered together, whenever that is.

Thus, I'm suggesting that it would be feasible to postpone discussion of hiding until such future time as a fully fleshed out renaming scheme is proposed. A revamped source-breaking import syntax without either hiding or renaming could be put in place now, and future addition of hiding and/or renaming would not have to be source-breaking. Is there something wrong with this argument?

There is still a useful to distinction to be made between explicitly renaming an API and explicitly hiding an API. Scala’s syntax to rename to underbar is a convenient notation for that kind of thing, but it goes against making qualified imports explicit and it means that renaming necessarily has to import identifiers into scope as well as rename them. What the OP (maybe it was you, sorry if it was) meant by “equivalent” missed the point that

import Swift hiding (String)

doesn’t translate into

import Swift renaming (String, to: _)

it translates into

import Swift hiding () renaming (String, to: _)

Renaming introducing identifiers into scope seems like a phase-shift and is not something the verb “rename” implies should happen here. It’s an interesting little hole in Agda’s module system that you can use

open A hiding (xs) renaming (ys to zs)

to mean

open A using (A; xs; ys) renaming (ys to zs)

Actually, scratch that. Their documentation explicitly mentions that hiding and renaming may not be mixed because of the phase distinction and recommend the using translation above as the way to go.

This is very illuminating. I think I've rather misunderstood what it is you're proposing. I wonder if others did also.

The syntax you proposed seemed cumbersome to me because my mental model of importing (informed by my probably superficial understanding of vanilla procedural programming languages) has only one phase: importing. This is why I proposed radically simplifying the spelling. To me, all of these operations are just sugar on a single import phase, where "stuff" from outside the module is "brought into" the module, either with the same name ("using"), a different name ("renaming"), or no name ("hiding").

But what you're saying here--if I understand correctly--is that you're proposing a multi-phase import system, where the possible phases, which can be composed in varying orders, are "using", "hiding", and "renaming". This is much, much more elaborate than I had contemplated. So beyond the bikeshedding of syntax, I'd ask: why do we need this multi-phase model of importing?

and as has been argued by others, the former is a special case of the latter.

A special case that cannot cause large-scale file-relative changes to APIs. Renaming is primarily used in other languages that treat free functions as more canonical than we do, or allow operator definitions that can be used as notation.

I don't know about 'primary use,' but the most common use I've experienced in Python, for example, is the mundane task of importing module Foo2 as Foo.

And I still want that kind of syntax. I just want to get the breaking changes out of the way to make room for it in the future.

Right. See above about my argument as to which parts of your proposal have to be source-breaking, and which don't.

In those cases, you often have your own notation you’d like to use. In Swift, such changes should be rare enough that if you can’t solve them with a disambiguating qualified import then you can just redeclare the identifier some other way (typealias, top-level let, wrapper class, whatever).

You've already stripped out renaming of members from the proposal. I agree wholeheartedly. The only flavor of renaming I'm thinking of here is equivalent to a fileprivate typealias and hiding, which cannot be done in this version of the proposal because hiding always comes before typealiasing and you can't typealias what isn't imported. It isn't about altering APIs any more than a fileprivate typealias can be thought of as altering APIs.

In the sense that you can’t use the original identifier if you rename it, it is an alteration. John brought up a great point about exporting these things and how it could be a potentially dangerous thing. Even used locally, there’s the potential for people to specify 500 lines of import renaming crap that has to be copypasta’d throughout the codebase to maintain that particular style - not a use-case I’ve ever seen, but the potential is there.

This is, I think, a spurious argument. I can equally have 500 lines of private typealiased crap that has to be copypasta'd.

On Wed, Jul 20, 2016 at 15:55 Brandon Knope <bknope@me.com> wrote:
I meant is there any reason for requiring parentheses

On Jul 20, 2016, at 4:53 PM, Robert Widmann <rwidmann@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@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@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@me.com> wrote:

On Jul 20, 2016, at 3:08 PM, Xiaodi Wu via swift-evolution <swift-evolution@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@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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

~Robert Widmann

2016/07/21 8:17、Félix Cloutier <felixcca@yahoo.ca <mailto:felixcca@yahoo.ca>> のメッセージ:

I know that the compiler can't read my mind. I don't mean to be frustrating.

If Foundation is imported in the bridging header of a project (as it is today, by default, in Swift framework projects), you get all of Foundation in all of your Swift code without a chance to write "import Foundation hiding (...)" anywhere. This reinforces my position that types should be selected affirmatively.

I don't understand this at all. The point of using and hiding is we create a file-local subset of identifiers you want to see. Bridging headers do not change that. I don't see how this is an example that affirmative is the *only* way to pick identifiers out of modules.

Here's a tiny Swift framework project: http://felixcloutier.com/tmp/SwiftImports.zip

The important part is that it has a Foo.h framework header, which #imports Cocoa. Through that, every Swift file in the project has access to everything that you get from <Cocoa/Cocoa.h>, as demonstrated by Foo.swift that creates a NSWindow without importing anything. Under the suggested model, I can't hide any of these symbols because the import does not happen on the Swift side, and there doesn't seem to be a way to break an ambiguity by hiding symbols in that case.

import Cocoa hiding (NSWindow)

Done. This specific case is one of the reasons why imports are file-local.

I probably misused "bridging header" because I don't fully understand how symbols flow between Objective-C and Swift in frameworks.

I'm not proposing a better solution because I don't have one right now. But if it would help appease you that I suggest something: keep "import using", let go of "hiding", and reuse the "using" keyword to select symbols from specific modules to break ambiguities. (Names must always be module-qualified to break ambiguities with classes that have the same name as a module.)

Halfway there. Why is hiding such an issue? Explain to me how with only using imports you can recreate the "import String without String.UTF8View" example in the proposal.

I'm pretty sure that I said that the example wasn't perfect. I wouldn't know how to do that. However, I don't know what else than breaking ambiguities you would want to use hiding for, and that example fully demonstrates how you would do that.

So it does. Thank you for mocking this up.

···

On Jul 21, 2016, at 9:25 AM, Félix Cloutier <felixcca@yahoo.ca> wrote:

Le 21 juil. 2016 à 08:48:22, Robert Widmann <devteam.codafi@gmail.com <mailto:devteam.codafi@gmail.com>> a écrit :

Félix