[Proposal][Discussion] Qualified Imports


(Beta) #1

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>, or as a gist <https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6>.

Thanks,

~Robert Widmann

Qualified Imports Revisited

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

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#introduction>Introduction

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

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#motivation>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.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#proposed-solution>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.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#detailed-design>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.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#impact-on-existing-code>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.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#alternatives-considered>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.


(Xiaodi Wu) #2

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 _, *)
···

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>, or as a gist
<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6>.

Thanks,

~Robert Widmann

Qualified Imports Revisited

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

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#introduction>
Introduction

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

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#motivation>
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.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#proposed-solution>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.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#detailed-design>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.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.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#impact-on-existing-code>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.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#alternatives-considered>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


(Joe Groff) #3

One thing to consider is that Swift has compound names, and also extension members, which are modularized but also scoped to the extended nominal type or protocol. Extension method collisions are a non-theoretical issue. Whatever bikeshed we pick, we'll want a syntax able to support something like this:

// Pick Int extension members from Module
import Module using Int.extensionProperty, Int.extensionMethod(label:)

Maybe there's also a need to pick a specific decl out of an overload set by type. I hope not…

-Joe

···

On Jul 20, 2016, at 11:52 AM, 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


(Colin Barrett) #4

Thanks for writing these up, I found them clear and easy to read.

While I don’t know the precise inspirations, both of these drafts seem more or less in line with the sorts of modules and importing that’s found in Haskell. I’d much prefer these facilities not be added to Swift. I would much rather see Swift develop in the direction of ML and add something like first-class modules and (module) functors. After writing a lot of both Haskell and ML family languages, I can say from experience that the Haskell-style approach is broadly inferior.

I have no idea if first-class modules are on the Core Team’s collective mind, but it’s what I’d advocate for.

-Colin

···

On Jul 20, 2016, at 2: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>, or as a gist <https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6>.

Thanks,

~Robert Widmann

Qualified Imports Revisited

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

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#introduction>Introduction

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

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#motivation>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.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#proposed-solution>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.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#detailed-design>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.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#impact-on-existing-code>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.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#alternatives-considered>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


(Pyry Jahkola) #5

Back to the pre-proposal itself,

I support taking it forward and it being accepted in some form. However, I remain unconvinced that we'd ever want `hiding` imports in Swift.

Here are a few specific comments on the text (8 in total):

Motivation

The existing syntax for qualified imports from modules is needlessly explicit, does not compose, and has a default semantics that dilutes the intended meaning of the very operation itself. Today, a qualified import looks something like this

import class Foundation.Date
This means that clients of Foundation that wish to see only Date must know the exact kind of declaration that identifier is.

1. Given this motivation, isn't it sufficient being able to import without knowing the exact kind of declaration? In other words, I agree we want `using` imports, but what motivates `hiding` imports really?

As it was pointed out by Félix, the use of `hiding` imports moves away the focus of what is imported into what is not imported, and while name conflict problems usually arise when importing another module on top of already working code, I think it's better to fix them at the import statement which brings in the winning name declaration, i.e. the `using` import.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#proposed-solution>Proposed solution

The grammar and semantics of qualified imports will change completely with the addition of import qualifiers and import directives. We also introduce two new contextual keywords: using and hiding, to facilitate fine-grained usage of module contents.

2. Is there a typo above? The proposal does not mention import qualifiers anywhere else. I guess those are part of what got postponed into later proposals.

Detailed design

1) using: The using directive is followed by a list of identifiers for non-member nominal declarations within the imported module that should be exposed to this file.

// The only visible parts of Foundation in this file are
// Foundation.Date, Foundation.DateFormatter, and Foundation.DateComponents
//
// Previously, this was
// import class Foundation.Date
// import class Foundation.DateFormatter
// import class Foundation.DateComponents
import Foundation using (Date, DateFormatter, DateComponents)

3. I support the idea of `using` imports. That's something we do need, and its introduction is sufficient to cover the existing use cases of the old selective import syntax about to be deprecated here.

2) hiding: The hiding directive is followed by a list of identifiers for non-member nominal declarations within the imported module that should be hidden from this file.

// Imports all of Foundation except `Date`
import Foundation hiding (Date)
As today, all hidden identifiers do not hide the type, they merely hide that type’s members and its declaration. For example, this means values of hidden types are still allowed. Unlike the existing implementation, using their members is forbidden.

4. Hmm, the above design detail is easily missed when reading the proposal. So what you're proposing is that the hiding of Date essentially turns Date into a type that the code will know almost nothing about, except it's something we can pass to other types' methods that expect a Date, am I right?

What's the benefit in that? Do you anticipate it will make importing lighter somehow, possibly improving compilation or library loading speed? You mentioned something along those lines in the Motivation, but I'm not sure if I got it right here.

Import directives chain to one another and can be used to create a fine-grained module import:

// This imports Swift.Int, Swift.Double, and Swift.String but hides Swift.String.UTF8View
import Swift using (String, Int, Double)
             hiding (String.UTF8View)
Directive chaining occurs left-to-right:

// This says to 1) Use Int 2) Hide String 3) rename Double to Triple. It is invalid
// because 1) Int is available 2) String is not, error.
import Swift using (Int) hiding (String)
// Valid. This will be merged as `using (Int)`
import Swift using () using (Int)
// Valid. This will be merged as `hiding (String, Double)`
import Swift hiding (String) hiding (Double) hiding ()
// Valid (if redundant). This will be merged as `using ()`
import Swift using (String) hiding (String)

5. These chaining rules do fit on the back of a napkin, but I'm not sure if we need them at all. I'm not convinced we need `hiding` imports, and without hiding imports, we need no rules for the order of imports.

Because import directives are file-local, they will never be exported along with the module that declares them.

6. +1 on imports being file-local, that's essentially what we have today.

What I will keep suggesting is that `using` imports actually take up the name in the file-local scope such that nothing else in the same file's scope — be it another `import ... using (...)`, a local type declaration, function, or value — can declare the same name with a different meaning. That way, a plain

    import Foo

can import everything from Foo, while another

    import Foo using (Bar)

can be used to explicitly choose the Bar the code is about to use.

7. (Off-topic to this proposal.) Given that you briefly expressed the interest in turning imports `public` or `internal` — some of which would indeed be useful in a better module system —, I think it's curious in that design that the default visibility level of imports would likely be `fileprivate`, which is different from the rest of Swift. That said, I think it's absolutely right for imports to remain file-local by default, because explicit tends to be better than implicit.

Impact on existing code

Existing code that is using qualified module import syntax (import {func|class|typealias|class|struct|enum|protocol} <qualified-name>) will be deprecated and should be removed or migrated.

8. +1 on deprecating the old selective import syntax.

Thank you for driving this forward!

— Pyry


(Brandon Knope) #6

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 Jul 20, 2016, at 3:08 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:

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


(Leonardo Pessoa) #7

I'm not very fond of this proposal at all as I don't believe there
will be much gain with it (correct me if I'm wrong but libraries in
Swift are monolithic and nothing to Java, where there is a benefit in
doing this). That said, if this really has to go, Joe's syntax seems
much cleaner but I'd drop the requirement for a dot and implicitly
require anything by omiting the parenthesis. Thus:

   import Swift
   import Swift(Int as MyInt, *)
   import Swift(Int as _, *)

Also supporting this form does not break existing code since the first
option here is how we already do.

L

···

On 20 July 2016 at 16:08, 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 _, *)

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


(Beta) #8

Max and I had a discussion about how overloading would work here. We came to the conclusion that we probably don’t need an “overload picker” because it defeats the point of an overloaded name in the first place.

As for the extension members, the current qualified import mechanism doesn’t allow you to get that specific and I don’t see a reason why we should either. Plus we dropped member imports from the revision :slight_smile:

That can come later as an additive change on top of this system.

···

On Jul 20, 2016, at 5:53 PM, Joe Groff <jgroff@apple.com> wrote:

One thing to consider is that Swift has compound names, and also extension members, which are modularized but also scoped to the extended nominal type or protocol. Extension method collisions are a non-theoretical issue. Whatever bikeshed we pick, we'll want a syntax able to support something like this:

// Pick Int extension members from Module
import Module using Int.extensionProperty, Int.extensionMethod(label:)

Maybe there's also a need to pick a specific decl out of an overload set by type. I hope not…

-Joe

On Jul 20, 2016, at 11:52 AM, 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


(Beta) #9

This approach in no way rules out that direction and the syntax here was built with a module system in mind. Agda modules admit the kind of abstraction you’re looking for, but they call them Parameterized Modules and they take after Coq’s sections rather than SML’s module functors. I can assure you, this proposal has nothing to do with Haskell and everything to do with trying to approach scoping and imports with their mindset.

···

On Jul 20, 2016, at 10:18 PM, Colin Barrett <colin@springsandstruts.com> wrote:

Thanks for writing these up, I found them clear and easy to read.

While I don’t know the precise inspirations, both of these drafts seem more or less in line with the sorts of modules and importing that’s found in Haskell. I’d much prefer these facilities not be added to Swift. I would much rather see Swift develop in the direction of ML and add something like first-class modules and (module) functors. After writing a lot of both Haskell and ML family languages, I can say from experience that the Haskell-style approach is broadly inferior.

I have no idea if first-class modules are on the Core Team’s collective mind, but it’s what I’d advocate for.

-Colin

On Jul 20, 2016, at 2: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>, or as a gist <https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6>.

Thanks,

~Robert Widmann

Qualified Imports Revisited

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

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#introduction>Introduction

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

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#motivation>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.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#proposed-solution>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.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#detailed-design>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.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#impact-on-existing-code>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.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#alternatives-considered>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 <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Beta) #10

~Robert Widmann

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

Back to the pre-proposal itself,

I support taking it forward and it being accepted in some form. However, I remain unconvinced that we'd ever want `hiding` imports in Swift.

Here are a few specific comments on the text (8 in total):

Motivation

The existing syntax for qualified imports from modules is needlessly explicit, does not compose, and has a default semantics that dilutes the intended meaning of the very operation itself. Today, a qualified import looks something like this

import class Foundation.Date
This means that clients of Foundation that wish to see only Date must know the exact kind of declaration that identifier is.

1. Given this motivation, isn't it sufficient being able to import without knowing the exact kind of declaration? In other words, I agree we want `using` imports, but what motivates `hiding` imports really?

As it was pointed out by Félix, the use of `hiding` imports moves away the focus of what is imported into what is not imported, and while name conflict problems usually arise when importing another module on top of already working code, I think it's better to fix them at the import statement which brings in the winning name declaration, i.e. the `using` import.

Proposed solution

The grammar and semantics of qualified imports will change completely with the addition of import qualifiers and import directives. We also introduce two new contextual keywords: using and hiding, to facilitate fine-grained usage of module contents.

2. Is there a typo above? The proposal does not mention import qualifiers anywhere else. I guess those are part of what got postponed into later proposals.

It is.

Detailed design

1) using: The using directive is followed by a list of identifiers for non-member nominal declarations within the imported module that should be exposed to this file.

// The only visible parts of Foundation in this file are
// Foundation.Date, Foundation.DateFormatter, and Foundation.DateComponents
//
// Previously, this was
// import class Foundation.Date
// import class Foundation.DateFormatter
// import class Foundation.DateComponents
import Foundation using (Date, DateFormatter, DateComponents)

3. I support the idea of `using` imports. That's something we do need, and its introduction is sufficient to cover the existing use cases of the old selective import syntax about to be deprecated here.

2) hiding: The hiding directive is followed by a list of identifiers for non-member nominal declarations within the imported module that should be hidden from this file.

// Imports all of Foundation except `Date`
import Foundation hiding (Date)
As today, all hidden identifiers do not hide the type, they merely hide that type’s members and its declaration. For example, this means values of hidden types are still allowed. Unlike the existing implementation, using their members is forbidden.

4. Hmm, the above design detail is easily missed when reading the proposal. So what you're proposing is that the hiding of Date essentially turns Date into a type that the code will know almost nothing about, except it's something we can pass to other types' methods that expect a Date, am I right?

What's the benefit in that? Do you anticipate it will make importing lighter somehow, possibly improving compilation or library loading speed? You mentioned something along those lines in the Motivation, but I'm not sure if I got it right here.

It is so that use of an API in a framework like Foundation does not necessitate polluting your Swift files with a million using imports - thus defeating the purpose of this proposal. DateFormatter alone needs to know about Date, String, TimeInterval, Calendar, Locale, and TimeZone. Each of those needs to know about additional components. To require an import decl to use these types explicitly would be madness. You'd just wind up importing Foundation anyway.

Import directives chain to one another and can be used to create a fine-grained module import:

// This imports Swift.Int, Swift.Double, and Swift.String but hides Swift.String.UTF8View
import Swift using (String, Int, Double)
             hiding (String.UTF8View)
Directive chaining occurs left-to-right:

// This says to 1) Use Int 2) Hide String 3) rename Double to Triple. It is invalid
// because 1) Int is available 2) String is not, error.
import Swift using (Int) hiding (String)
// Valid. This will be merged as `using (Int)`
import Swift using () using (Int)
// Valid. This will be merged as `hiding (String, Double)`
import Swift hiding (String) hiding (Double) hiding ()
// Valid (if redundant). This will be merged as `using ()`
import Swift using (String) hiding (String)

5. These chaining rules do fit on the back of a napkin, but I'm not sure if we need them at all. I'm not convinced we need `hiding` imports, and without hiding imports, we need no rules for the order of imports.

How else are we to allow you to import a using type but not any of its member types? How could we support removing APIs related to NSCell in AppKit apps? How about NSStream everywhere else? How else can we allow, in the future, the ability to import NSObject but none of its KVO-related members? A hiding import is an invariant: It says a particular API should never be considered for use in this file. The very act of 'using' an identifier means you are hiding all others. The very act of 'hiding' an identifier means you are using all others.

Because import directives are file-local, they will never be exported along with the module that declares them.

6. +1 on imports being file-local, that's essentially what we have today.

What I will keep suggesting is that `using` imports actually take up the name in the file-local scope such that nothing else in the same file's scope — be it another `import ... using (...)`, a local type declaration, function, or value — can declare the same name with a different meaning. That way, a plain

    import Foo

can import everything from Foo, while another

    import Foo using (Bar)

can be used to explicitly choose the Bar the code is about to use.

That was the plan. You will receive an "invalid redeclaration" error as always.

···

7. (Off-topic to this proposal.) Given that you briefly expressed the interest in turning imports `public` or `internal` — some of which would indeed be useful in a better module system —, I think it's curious in that design that the default visibility level of imports would likely be `fileprivate`, which is different from the rest of Swift. That said, I think it's absolutely right for imports to remain file-local by default, because explicit tends to be better than implicit.

Impact on existing code

Existing code that is using qualified module import syntax (import {func|class|typealias|class|struct|enum|protocol} <qualified-name>) will be deprecated and should be removed or migrated.

8. +1 on deprecating the old selective import syntax.

Thank you for driving this forward!

— Pyry


(Colin Barrett) #11

I'm not sure how any of that's relevant to what I said. (I'm on my phone. Please excuse any incoherence or typos.) Your module system from the other proposal is, like in Haskell, tied to the directory structure of the project, which is in my mind an anti-pattern. And I don't believe either SML or OCaml have a way to selectively import (open in ML terms), much less do renaming. Using ML style modules a bunch has me convinced. Beyond first-class modules, the key aspects of ML germane to this proposal in my mind are: qualified import by default (module statement) and separating identifier search (open statement) from re-exporting (include statement). Local open is also a big deal.

For anyone else in the thread curious about ML modules, my friend Danny wrote this up a while ago, it's quite good http://jozefg.bitbucket.org/posts/2015-01-08-modules.html

-Colin (via thumbs)

···

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

This approach in no way rules out that direction and the syntax here was built with a module system in mind. Agda modules admit the kind of abstraction you’re looking for, but they call them Parameterized Modules and they take after Coq’s sections rather than SML’s module functors. I can assure you, this proposal has nothing to do with Haskell and everything to do with trying to approach scoping and imports with their mindset.

On Jul 20, 2016, at 10:18 PM, Colin Barrett <colin@springsandstruts.com> wrote:

Thanks for writing these up, I found them clear and easy to read.

While I don’t know the precise inspirations, both of these drafts seem more or less in line with the sorts of modules and importing that’s found in Haskell. I’d much prefer these facilities not be added to Swift. I would much rather see Swift develop in the direction of ML and add something like first-class modules and (module) functors. After writing a lot of both Haskell and ML family languages, I can say from experience that the Haskell-style approach is broadly inferior.

I have no idea if first-class modules are on the Core Team’s collective mind, but it’s what I’d advocate for.

-Colin

On Jul 20, 2016, at 2: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


(Pyry Jahkola) #12

I think we're getting somewhere.

(An excerpt about the hiding directive <https://github.com/CodaFi/swift-evolution/blob/ab091043daa62158bd3337a2a2a467be3e16ff18/proposals/XXXX-qualified-imports.md#detailed-design>)

4. Hmm, the above design detail is easily missed when reading the proposal. So what you're proposing is that the hiding of Date essentially turns Date into a type that the code will know almost nothing about, except it's something we can pass to other types' methods that expect a Date, am I right?

What's the benefit in that? Do you anticipate it will make importing lighter somehow, possibly improving compilation or library loading speed? You mentioned something along those lines in the Motivation, but I'm not sure if I got it right here.

It is so that use of an API in a framework like Foundation does not necessitate polluting your Swift files with a million using imports - thus defeating the purpose of this proposal.

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

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

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

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

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

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

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

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

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

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

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

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

5. These chaining rules do fit on the back of a napkin, but I'm not sure if we need them at all. I'm not convinced we need `hiding` imports, and without hiding imports, we need no rules for the order of imports.

How else are we to allow you to import a using type but not any of its member types?

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

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

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

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

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

What I will keep suggesting is that `using` imports actually take up the name in the file-local scope such that nothing else in the same file's scope — be it another `import ... using (...)`, a local type declaration, function, or value — can declare the same name with a different meaning. That way, a plain

    import Foo

can import everything from Foo, while another

    import Foo using (Bar)

can be used to explicitly choose the Bar the code is about to use.

That was the plan. You will receive an "invalid redeclaration" error as always.

:+1:!

— Pyry

···

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


(Joe Groff) #13

Max and I had a discussion about how overloading would work here. We came to the conclusion that we probably don’t need an “overload picker” because it defeats the point of an overloaded name in the first place.

As for the extension members, the current qualified import mechanism doesn’t allow you to get that specific and I don’t see a reason why we should either. Plus we dropped member imports from the revision :slight_smile:

I suppose it can be added later, but I consider the inability to deal with extension method conflicts to be a pretty serious language shortcoming today. It would be nice to fix that.

-Joe

···

On Jul 20, 2016, at 5:56 PM, Robert Widmann <rwidmann@apple.com> wrote:

That can come later as an additive change on top of this system.

On Jul 20, 2016, at 5:53 PM, Joe Groff <jgroff@apple.com> wrote:

One thing to consider is that Swift has compound names, and also extension members, which are modularized but also scoped to the extended nominal type or protocol. Extension method collisions are a non-theoretical issue. Whatever bikeshed we pick, we'll want a syntax able to support something like this:

// Pick Int extension members from Module
import Module using Int.extensionProperty, Int.extensionMethod(label:)

Maybe there's also a need to pick a specific decl out of an overload set by type. I hope not…

-Joe

On Jul 20, 2016, at 11:52 AM, 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


(Xiaodi Wu) #14

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>, or as a gist
<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6>.

Thanks,

~Robert Widmann

Qualified Imports Revisited

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

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#introduction>
Introduction

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

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#motivation>
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.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#proposed-solution>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.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#detailed-design>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.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.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#impact-on-existing-code>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.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#alternatives-considered>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


(Robert Widmann) #15

~Robert Widmann

2016/07/20 12:19、Leonardo Pessoa via swift-evolution <swift-evolution@swift.org> のメッセージ:

I'm not very fond of this proposal at all as I don't believe there
will be much gain with it (correct me if I'm wrong but libraries in
Swift are monolithic and nothing to Java, where there is a benefit in
doing this). That said, if this really has to go, Joe's syntax seems
much cleaner but I'd drop the requirement for a dot and implicitly
require anything by omiting the parenthesis. Thus:

  import Swift
  import Swift(Int as MyInt, *)
  import Swift(Int as _, *)

Also supporting this form does not break existing code since the first
option here is how we already do.

Reread the proposal please. Qualified import syntax is being subsumed by ours (and Joe's) syntax. This is a breaking change.

Second, while this is more concise, it is not more semantically meaningful and does not reasonably extend to any other operations. It does not fit any existing syntax in Swift outside of compiler attributes (of which import is not one), and assumes that renaming is a common operation without actually specifying how it works with re-export.

In short: I can't read this. It's small, it's convenient, but it's semantically meaningless.

···

L

On 20 July 2016 at 16:08, 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 _, *)

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


(Robert Widmann) #16

~Robert Widmann

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

I think we're getting somewhere.

(An excerpt about the hiding directive)

4. Hmm, the above design detail is easily missed when reading the proposal. So what you're proposing is that the hiding of Date essentially turns Date into a type that the code will know almost nothing about, except it's something we can pass to other types' methods that expect a Date, am I right?

What's the benefit in that? Do you anticipate it will make importing lighter somehow, possibly improving compilation or library loading speed? You mentioned something along those lines in the Motivation, but I'm not sure if I got it right here.

It is so that use of an API in a framework like Foundation does not necessitate polluting your Swift files with a million using imports - thus defeating the purpose of this proposal.

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

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

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

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

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

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

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

Funny, others have been suggesting _ be used as a special namespace to hide identifiers in. It implies you don't care about identifiers, not that you want all of them.

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

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

I should be more clear: If we requires you to qualified import types without these allowances then you would have to recursively import types out of Foundation until you wind up importing the whole thing anyway. To be able to use DateFormatter you shouldn't have to tell Swift you also want to use Date, String, TimeInterval, etc. We know you want to, we can see all of these identifiers. When the time comes for you to pick a member on Date, then you want a using or hiding import to let us know you want to see it.

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

The grammar makes no allowances for types to be imported there, only qualified module name. Not sure what you mean.

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

And additive and can be introduced in a future proposal as a new directive on top of this system. We wanted this, the community had reservations, we backed off.

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

We need to be able to disambiguate more than just top-level names, for one. Hiding can do that.

5. These chaining rules do fit on the back of a napkin, but I'm not sure if we need them at all. I'm not convinced we need `hiding` imports, and without hiding imports, we need no rules for the order of imports.

How else are we to allow you to import a using type but not any of its member types?

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

That is why, just rhetorical.

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

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

Fair.

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

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

I have given one. Show me how to express the "give me String but not String.UTF8View" example without hiding?

···

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

What I will keep suggesting is that `using` imports actually take up the name in the file-local scope such that nothing else in the same file's scope — be it another `import ... using (...)`, a local type declaration, function, or value — can declare the same name with a different meaning. That way, a plain

    import Foo

can import everything from Foo, while another

    import Foo using (Bar)

can be used to explicitly choose the Bar the code is about to use.

That was the plan. You will receive an "invalid redeclaration" error as always.

:+1:!

— Pyry

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


(Robert Widmann) #17

~Robert Widmann

2016/07/21 8:21、Colin Barrett via swift-evolution <swift-evolution@swift.org> のメッセージ:

I'm not sure how any of that's relevant to what I said. (I'm on my phone. Please excuse any incoherence or typos.) Your module system from the other proposal is, like in Haskell, tied to the directory structure of the project, which is in my mind an anti-pattern.

This thread is about qualified imports. I initially thought you mean the import system wouldn't scale to accommodate ML-style modules. Now I see this is just about modules. We need to postpone this discussion until that proposal comes up, or use the old thread to voice this please.

And I don't believe either SML or OCaml have a way to selectively import (open in ML terms), much less do renaming. Using ML style modules a bunch has me convinced. Beyond first-class modules, the key aspects of ML germane to this proposal in my mind are: qualified import by default (module statement) and separating identifier search (open statement) from re-exporting (include statement). Local open is also a big deal.

It is a big deal, we know about it, and we wanted it. It is out of scope for this proposal.

For anyone else in the thread curious about ML modules, my friend Danny wrote this up a while ago, it's quite good http://jozefg.bitbucket.org/posts/2015-01-08-modules.html

Seconded. Fine blog.

···

-Colin (via thumbs)

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

This approach in no way rules out that direction and the syntax here was built with a module system in mind. Agda modules admit the kind of abstraction you’re looking for, but they call them Parameterized Modules and they take after Coq’s sections rather than SML’s module functors. I can assure you, this proposal has nothing to do with Haskell and everything to do with trying to approach scoping and imports with their mindset.

On Jul 20, 2016, at 10:18 PM, Colin Barrett <colin@springsandstruts.com> wrote:

Thanks for writing these up, I found them clear and easy to read.

While I don’t know the precise inspirations, both of these drafts seem more or less in line with the sorts of modules and importing that’s found in Haskell. I’d much prefer these facilities not be added to Swift. I would much rather see Swift develop in the direction of ML and add something like first-class modules and (module) functors. After writing a lot of both Haskell and ML family languages, I can say from experience that the Haskell-style approach is broadly inferior.

I have no idea if first-class modules are on the Core Team’s collective mind, but it’s what I’d advocate for.

-Colin

On Jul 20, 2016, at 2: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


(Beta) #18

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 <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>, or as a gist <https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6>.

Thanks,

~Robert Widmann

Qualified Imports Revisited

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

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#introduction>Introduction

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

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#motivation>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.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#proposed-solution>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.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#detailed-design>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.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#impact-on-existing-code>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.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#alternatives-considered>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 <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


(Brandon Knope) #19

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


(Brandon Knope) #20

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