[Proposal] Qualified Imports and Modules


(Beta) #1

Hello all,

TJ Usiyan, Harlan Haskins, and I have been working on a proposal to rework qualified imports and introduce an explicit module system to Swift that we’d like to publish for your viewing pleasure.

The initial impetus was set out in a radar (rdar://17630570) I sent fairly early on that didn’t receive a response, so I started a swift-evolution <http://permalink.gmane.org/gmane.comp.lang.swift.evolution/1378> thread discussing the basics of this proposal. It has been refined and expanded a bit to include an effort to make Swift modules explicit and updated with the feedback of that first thread. Contents of the proposal are inline and can also be had as a gist <https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6> or on Github. <https://github.com/apple/swift-evolution/pull/440>

Cheers,

~Robert Widmann

Qualified Imports and Modules

Proposal: SE-NNNN <https://gist.github.com/CodaFi/NNNN-first-class-qualified-imports.md>
Authors: Robert Widmann <https://github.com/codafi>, Harlan Haskins <https://github.com/harlanhaskins>, 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 and the introduction of a module system.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#motivation>Motivation

Swift code is modular by default. However, it is not clear how to decompose existing modules further into submodules. In addition, it is difficult to tell how importing a module affects its export to consumers of a library. This leads many to either fake namespaces with enums, attempt to structure Swift code with modulemaps, or use a large amount of version-control submodules. All of these can be rolled into one complete package in the form of a comprehensive rethink of the qualified import system and the introduction of a module system.

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

Modules will now become an explicit part of working with canonical Swift code. The grammar and semantics of qualified imports will change completely with the addition of import qualifiers and import directives. We also introduce three new contextual keywords: using, hiding, and renaming, 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

module-decl -> module <module-path>
import-decl -> <access-level-modifier> import <module-path> <(opt) import-directive-list>
module-path -> <identifier>
            -> <identifier>.<import-path>
import-directive-list -> <import-directive>
                      -> <import-directive> <import-directive-list>
import-directive -> using (<identifier>, ...)
                 -> hiding (<identifier>, ...)
                 -> renaming (<identifier>, to: <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 3 operations:

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

// The only visible parts of Foundation in this file are
// Date.init(), Date.hashValue, and Date.description.
import Foundation.Date using (Date.init(), Date.hashValue, Date.description)
2) hiding: The hiding directive is followed by a list of identifiers within the imported module that should be hidden from this file.

// Imports all of Foundation.Date except `Date.compare()`
import Foundation.Date hiding (Date.compare())
3) renaming: The renaming directive is followed by a list of identifiers separated by to: that should be exposed to this file but renamed.

// Imports all of Dispatch.DispatchQueue but renames the static member
// DispatchQueue.main, to DispatchQueue.mainQueue
import Dispatch.DispatchQueue renaming (DispatchQueue.Type.main to: DispatchQueue.Type.mainQueue)
// Renaming can also rename modules. All members of UIKit have to be qualified with
// `UI` now.
import UIKit renaming (UIKit, to: UI)
Import directives chain to one another and can be used to create a fine-grained module import:

// Imports all of Foundation except `DateFormatter` and renames `Cache` to `LRUCache`
import Foundation hiding (DateFormatter) renaming (Cache to: LRUCache)
// Imports SCNNode except SCNNode.init(mdlObject:) and renames `.description` to
// `.nodeDescription`
import SceneKit using (SCNNode)
                renaming (SCNNode.description, to: SCNNode.nodeDescription)
                hiding (SCNNode.init(mdlObject:))
Directive chaining occurs left-to-right:

// This says to 1) Hide nothing 2) Use nothing 3) rename Int to INT. It is invalid
// because 1) We will show everything 2) Then hide everything 3) Therefore Int is unavailable, error.
import Swift hiding () using () renaming (Int, to: INT)
// 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. 3) Double is unavailable, error.
import Swift using (Int) hiding (String) renaming (Double, to: Triple)
// 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)
Module scope is delimited by the keyword module followed by a fully qualified name and must occur as the first declaration in a file. For example:

// ./Math/Integers/Arithmetic.swift
module Math.Integers.Arithmetic

public protocol _IntegerArithmetic {}

public struct _Abs {}

@_versioned
internal func _abs<Args>(_ args: Args) -> (_Abs, Args) {}

// ./Math/Integers.swift
module Math.Integers

// _abs is visible in this module and all others within the project,
// but is not exported along with it.
internal import Math.Integers.Arithmetic

public protocol IntegerArithmetic : _IntegerArithmetic, Comparable {}
public protocol SignedNumber : Comparable, ExpressibleByIntegerLiteral {}

// Math.swift
module Math

// Exports the entire public contents of Math.Integers, but nothing in
// Math.Integers.Arithmetic.
public import Math.Integers
Modules names are tied to a directory structure that describes their location relative to the current module and it will now be an error to violate this rule. For example:

module String // lives in ./String.swift
module String.Core // lives in ./String/Core.swift
module String.Core.Internals.Do.You.Even.Write // lives in ./String/Core/Internals/Do/You/Even/Write.swift
Existing projects that do not adopt these rules will still retain their implicit module name (usually defined as the name of the framework or application that is being built) and may continue to use whatever directory structure they wish, however they may not declare any explicit modules.

This proposal also solves the problem of module export. A module that is imported without an access level modifier will default to an internal import per usual. However, when it is useful to fully expose the public content of submodules to a client, a public modifier can be used. Similarly, when it is useful to access internal or [file]private APIs, but not expose them to clients, those access modifiers may be used. The rule of thumb is: Only identifiers that are at least as visible as the qualifier on the import make for valid import declarations. For example:

// A submodule declaring a `private` class that gets imported with
// an `internal` qualifier with a `using` directive is an invalid import
// declaration.
module Foo.Bar

private class PrivateThing {}

module Foo

// Error: PrivateThing not visible, use `private import`
import Foo.Bar using (PrivateThing)
// However, a submodule declaring a `public` struct that gets imported with
// an `private` qualifier is a valid import declaration.
module Foo.Bar

public class PublicThing {}

module Foo

// All good! Foo can see Foo.Bar.PrivateThing.
private import Foo.Bar using (PublicThing)
Because import directives are file-local, they will never be exported along with a public import and will default to exporting the entire contents of the module as though you had never declared them.

// In this file and this file alone, the directives apply. To the user
// of this module, it is as though this declaration were simply:
// public import Foundation.Date
public import Foundation.Date hiding (Date.init())
                              renaming (Date.Type.distantPast,
                                        Date.Type.timeIntervalSinceReferenceDate,
                              renaming (Date.Type.<, to: Date.Type.<<<<<)
<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. Code that is not organized into modules will remain unaffected and organized into one contiguous top-level module. However, it is strongly recommended that frameworks be decomposed and reorganized around the new module system.

As a case study, the public interface to the standard library appears to already be mostly broken down into submodules as described in GroupInfo.json <https://github.com/apple/swift/blob/master/stdlib/public/core/GroupInfo.json>.

Code that is defined in modulemaps already defines a module structure that can be imported directly into this scheme.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#alternatives-considered>Alternatives considered

Module export can also be placed on the module declaration itself. The relevant parts of the grammar that have changed are below with an example:

module-decl -> <access-level-modifier> module <module-path>
import-decl -> import <module-path> <(opt) import-directive-list>
private module String.Core.Internals

// Shh, it's a secret.
While this style makes it immediately obvious to the library author which modules are public or private, it causes the consumer problems because submodule exports are no longer explicit and are entirely ad-hoc. In the interest of enabling, for one, users of IDEs to drill into public submodules, making export local to import seems more appropriate.

···

to: Date.Type.letsGoLivingInThePast,
                                        to: Date.Type.startOfTheUniverse)


The One Stop Shop for Previous Submodule Pitches
The One Stop Shop for Previous Submodule Pitches
(John McCall) #2

Hello all,

TJ Usiyan, Harlan Haskins, and I have been working on a proposal to rework qualified imports and introduce an explicit module system to Swift that we’d like to publish for your viewing pleasure.

The initial impetus was set out in a radar (rdar://17630570 <rdar://17630570>) I sent fairly early on that didn’t receive a response, so I started a swift-evolution <http://permalink.gmane.org/gmane.comp.lang.swift.evolution/1378> thread discussing the basics of this proposal. It has been refined and expanded a bit to include an effort to make Swift modules explicit and updated with the feedback of that first thread. Contents of the proposal are inline and can also be had as a gist <https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6> or on Github. <https://github.com/apple/swift-evolution/pull/440>

Cheers,

~Robert Widmann

Qualified Imports and Modules

Proposal: SE-NNNN <https://gist.github.com/CodaFi/NNNN-first-class-qualified-imports.md>
Authors: Robert Widmann <https://github.com/codafi>, Harlan Haskins <https://github.com/harlanhaskins>, 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 and the introduction of a module system.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#motivation>Motivation

Swift code is modular by default. However, it is not clear how to decompose existing modules further into submodules. In addition, it is difficult to tell how importing a module affects its export to consumers of a library. This leads many to either fake namespaces with enums, attempt to structure Swift code with modulemaps, or use a large amount of version-control submodules. All of these can be rolled into one complete package in the form of a comprehensive rethink of the qualified import system and the introduction of a module system.

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

Modules will now become an explicit part of working with canonical Swift code. The grammar and semantics of qualified imports will change completely with the addition of import qualifiers and import directives. We also introduce three new contextual keywords: using, hiding, and renaming, 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

module-decl -> module <module-path>
import-decl -> <access-level-modifier> import <module-path> <(opt) import-directive-list>
module-path -> <identifier>
            -> <identifier>.<import-path>
import-directive-list -> <import-directive>
                      -> <import-directive> <import-directive-list>
import-directive -> using (<identifier>, ...)
                 -> hiding (<identifier>, ...)
                 -> renaming (<identifier>, to: <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 3 operations:

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

// The only visible parts of Foundation in this file are
// Date.init(), Date.hashValue, and Date.description.
import Foundation.Date using (Date.init(), Date.hashValue, Date.description)
2) hiding: The hiding directive is followed by a list of identifiers within the imported module that should be hidden from this file.

// Imports all of Foundation.Date except `Date.compare()`
import Foundation.Date hiding (Date.compare())

It's unfortunate that this proposal requires the identifiers to be re-qualified when the imported module is actually the name of a type. It seems to me that whether e.g. a type is defined in its own sub-module is a detail that users won't really appreciate and which probably shouldn't be surfaced to them. In fact, in general I'm concerned about this turning the file and directory organization of a project into API.

This proposal also solves the problem of module export. A module that is imported without an access level modifier will default to an internal import per usual. However, when it is useful to fully expose the public content of submodules to a client, a public modifier can be used. Similarly, when it is useful to access internal or [file]private APIs, but not expose them to clients, those access modifiers may be used.

These uses of access modifiers feel inconsistent to me. "public import Foo" exports the contents of Foo as if they were members of my module, but "private import Foo" imports the private APIs (?) of Foo? That is not the same interpretive rule.

I think the more consistent analogy for "private import" would be to say that the public members of Foo are visible in this file only (i.e. the default behavior of "import" today, which I think we would want to keep), whereas you could do an "internal import" to make the members of Foo visible throughout the current module (potentially useful if you have an interesting set of common modifications you want to make).

I don't know why you think it should be possible to import the private declarations of a module. That seems completely contrary to the access-control design. I agree that it's useful to have sub-modules expose APIs that are only usable by other parts of the larger module, but I think the Swiftier design would be for that to be opt-in on the declaration somehow, or at least to specify how it interacts with "internal".

Also, it is completely unclear to me why modifiers like "renaming" don't change how the imported module's declarations are re-exported.

John.

···

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

The rule of thumb is: Only identifiers that are at least as visible as the qualifier on the import make for valid import declarations. For example:

// A submodule declaring a `private` class that gets imported with
// an `internal` qualifier with a `using` directive is an invalid import
// declaration.
module Foo.Bar

private class PrivateThing {}

module Foo

// Error: PrivateThing not visible, use `private import`
import Foo.Bar using (PrivateThing)
// However, a submodule declaring a `public` struct that gets imported with
// an `private` qualifier is a valid import declaration.
module Foo.Bar

public class PublicThing {}

module Foo

// All good! Foo can see Foo.Bar.PrivateThing.
private import Foo.Bar using (PublicThing)
Because import directives are file-local, they will never be exported along with a public import and will default to exporting the entire contents of the module as though you had never declared them.

// In this file and this file alone, the directives apply. To the user
// of this module, it is as though this declaration were simply:
// public import Foundation.Date
public import Foundation.Date hiding (Date.init())
                              renaming (Date.Type.distantPast,
                                        to: Date.Type.letsGoLivingInThePast,
                                        Date.Type.timeIntervalSinceReferenceDate,
                                        to: Date.Type.startOfTheUniverse)
                              renaming (Date.Type.<, to: Date.Type.<<<<<)
<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. Code that is not organized into modules will remain unaffected and organized into one contiguous top-level module. However, it is strongly recommended that frameworks be decomposed and reorganized around the new module system.

As a case study, the public interface to the standard library appears to already be mostly broken down into submodules as described in GroupInfo.json <https://github.com/apple/swift/blob/master/stdlib/public/core/GroupInfo.json>.

Code that is defined in modulemaps already defines a module structure that can be imported directly into this scheme.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#alternatives-considered>Alternatives considered

Module export can also be placed on the module declaration itself. The relevant parts of the grammar that have changed are below with an example:

module-decl -> <access-level-modifier> module <module-path>
import-decl -> import <module-path> <(opt) import-directive-list>
private module String.Core.Internals

// Shh, it's a secret.
While this style makes it immediately obvious to the library author which modules are public or private, it causes the consumer problems because submodule exports are no longer explicit and are entirely ad-hoc. In the interest of enabling, for one, users of IDEs to drill into public submodules, making export local to import seems more appropriate.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Brent Royal-Gordon) #3

Hello all,

TJ Usiyan, Harlan Haskins, and I have been working on a proposal to rework qualified imports and introduce an explicit module system to Swift that we’d like to publish for your viewing pleasure.

This is really interesting. A few incomplete comments:

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

// Imports all of Foundation.Date except `Date.compare()`
import Foundation.Date hiding (Date.compare())

[snip]

// Exports the entire public contents of Math.Integers, but nothing in
// Math.Integers.Arithmetic.
public import Math.Integers

Would this work?

  module UIKit
  
  public import UIKit.UIGestureRecognizerSubclass.UIGestureRecognizer
    hiding (UIGestureRecognizer.touchesBegan(_:with:), UIGestureRecognizer.touchesMoved(_:with:), …)

(If so, we'd need a way to hide only the setter of a property, not the getter.)

Modules names are tied to a directory structure that describes their location relative to the current module and it will now be an error to violate this rule. For example:

module String // lives in ./String.swift

module String.Core // lives in ./String/Core.swift

module String.Core.Internals.Do.You.Even.Write // lives in ./String/Core/Internals/Do/You/Even/Write.swift

I think this is a mistake for several reasons:

* You may need to split a single submodule across multiple files, but this rule doesn't allow that.

* The module declaration and filename contain redundant information, and one of them could get out of sync with the other.

* Xcode doesn't like to organize things into folders on disk and will fight you tooth and nail.

* Deeply nested folders are a usability issue. Never forget the jury in Oracle v. Google: https://www.geek.com/wp-content/uploads/2016/05/courtroomhijinks.png

At the very least, I would like to see allowances for multi-file submodules—String/Core/Internals/Do/You/Even/Write**.swift. Better would be to use long filenames—String.Core.Internals.Do.You.Even.Write*.swift. Even better would be to just allow freeform naming and trust programmers to organize their projects sanely.

Existing projects that do not adopt these rules will still retain their implicit module name (usually defined as the name of the framework or application that is being built) and may continue to use whatever directory structure they wish, however they may not declare any explicit modules.

This proposal also solves the problem of module export. A module that is imported without an access level modifier will default to an internal import per usual. However, when it is useful to fully expose the public content of submodules to a client, a public modifier can be used. Similarly, when it is useful to access internal or [file]private APIs, but not expose them to clients, those access modifiers may be used. The rule of thumb is: Only identifiers that are at least as visible as the qualifier on the import make for valid import declarations.

It appears the semantics here are "grab all declarations at level X and above, and expose them in this module as level X". This is bizarre for a number of reasons:

* Currently, `import` can only access `internal` declarations using `@testable`. This would change that.

* Currently, `import` can't access `private` and `fileprivate` declarations at all, and it's not clear what it would mean to add that. What happens if two different parts of the module have different `private` members with the same name? Which do you get?

* Currently, `import` only affects the current file—it's effectively "import `public` members as `fileprivate`". If your default is `internal import`, that would imply that an import statement in one file would, by default, expose the APIs it imported to all files. That's an important change in behavior.

I think you're mixing two things together that ought not to be. `import` should always import only public APIs (unless you use `@testable`—which might need a bit of renaming to support the additional use case of SPIs between submodules and supermodules—in which case you also get `internal` APIs). An access modifier on the `import` statement controls how they're exposed to the rest of the file/project/world, and `private` is the default. It's a little weird to have `private` be the default on `import` when `internal` is the default on everything else, but the alternative is to change `import`'s behavior in a way that is neither backwards-compatible, nor likely to be correct.

Because import directives are file-local, they will never be exported along with a public import and will default to exporting the entire contents of the module as though you had never declared them.

Whoa, wait, what? Why? I think hiding parts of the implementation is a good use case for re-exporting a module. And I think the clear implication of attaching these clauses directly to the import statement is that it controls how the module is imported everywhere that statement makes it visible, not just within the one file. If we're not going to do that, I think these clauses ought to be separated from the `import` statement and turned into something separate.

···

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

--
Brent Royal-Gordon
Architechies


(Joe Groff) #4

Our import story definitely needs work, and this is a step in the right direction. Thanks for working on this! Some comments:

- The import changes can be separated from the submodule issues. Enhancing imports is IMO more important, and is source-breaking today, whereas 'module ' declarations and submodules can be added later. I'd suggest breaking this into two proposals.
- I think the `import` design you propose is a bit more complicated than it needs to be. Python and Haskell get by just having "import everything" and "import itemized (with aliases)". I don't see the need for 'hiding'; if you have a rule that itemized imports get priority over import-everything, then that covers the most important use case of selectively shadowing one module's imports with another. Bikeshed-wise, I don't see much reason to veer from the Java/Haskell-ish template of:

import Foo.* // import everything from module Foo
import Foo.(x, y, z as zed) // import x, y, and z from foo, renaming Foo.z to zed

-Joe

···

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

Hello all,

TJ Usiyan, Harlan Haskins, and I have been working on a proposal to rework qualified imports and introduce an explicit module system to Swift that we’d like to publish for your viewing pleasure.

The initial impetus was set out in a radar (rdar://17630570) I sent fairly early on that didn’t receive a response, so I started a swift-evolution thread discussing the basics of this proposal. It has been refined and expanded a bit to include an effort to make Swift modules explicit and updated with the feedback of that first thread. Contents of the proposal are inline and can also be had as a gist or on Github.

Cheers,

~Robert Widmann

Qualified Imports and Modules

  • Proposal: SE-NNNN
  • Authors: Robert Widmann, Harlan Haskins, TJ Usiyan
  • Status: Awaiting review
  • Review manager: TBD
Introduction

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

Motivation

Swift code is modular by default. However, it is not clear how to decompose existing modules further into submodules. In addition, it is difficult to tell how importing a module affects its export to consumers of a library. This leads many to either fake namespaces with enums, attempt to structure Swift code with modulemaps, or use a large amount of version-control submodules. All of these can be rolled into one complete package in the form of a comprehensive rethink of the qualified import system and the introduction of a module system.

Proposed solution

Modules will now become an explicit part of working with canonical Swift code. The grammar and semantics of qualified imports will change completely with the addition of import qualifiers and import directives. We also introduce three new contextual keywords: using, hiding, and renaming, to facilitate fine-grained usage of module contents.

Detailed design

Qualified import syntax will be revised to the following

module-decl -> module <module-path>
import-decl -> <access-level-modifier> import <module-path> <(opt) import-directive-list>
module-path -> <identifier>
            -> <identifier>.<import-path>
import-directive-list -> <import-directive>
                      -> <import-directive> <import-directive-list>
import-directive -> using (<identifier>, ...)
                 -> hiding (<identifier>, ...)
                 -> renaming (<identifier>, to: <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 3 operations:

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

// The only visible parts of Foundation in this file are
// Date.init(), Date.hashValue, and Date.description.
import Foundation.Date using (Date.init(), Date.hashValue, Date.description)
2) hiding: The hiding directive is followed by a list of identifiers within the imported module that should be hidden from this file.

// Imports all of Foundation.Date except `Date.compare()`
import Foundation.Date hiding (Date.compare())
3) renaming: The renaming directive is followed by a list of identifiers separated by to: that should be exposed to this file but renamed.

// Imports all of Dispatch.DispatchQueue but renames the static member
// DispatchQueue.main, to DispatchQueue.mainQueue
import Dispatch.DispatchQueue renaming (DispatchQueue.Type.main to: DispatchQueue.Type.
mainQueue)

// Renaming can also rename modules. All members of UIKit have to be qualified with
// `UI` now.
import UIKit renaming (UIKit, to: UI)
Import directives chain to one another and can be used to create a fine-grained module import:

// Imports all of Foundation except `DateFormatter` and renames `Cache` to `LRUCache`
import Foundation
hiding (DateFormatter) renaming (Cache to: LRUCache)

// Imports SCNNode except SCNNode.init(mdlObject:) and renames `.description` to
// `.nodeDescription`
import SceneKit
using (SCNNode)
                renaming (SCNNode
.description, to: SCNNode.
nodeDescription)
                hiding (SCNNode
.init(mdlObject:))
Directive chaining occurs left-to-right:

// This says to 1) Hide nothing 2) Use nothing 3) rename Int to INT. It is invalid
// because 1) We will show everything 2) Then hide everything 3) Therefore Int is unavailable, error.
import Swift hiding () using () renaming (Int
, to: INT)

// 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. 3) Double is unavailable, error.
import Swift using (Int) hiding (String) renaming (Double
, to: Triple)

// 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)
Module scope is delimited by the keyword module followed by a fully qualified name and must occur as the first declaration in a file. For example:

// ./Math/Integers/Arithmetic.swift

module Math
.Integers.
Arithmetic

public protocol
_IntegerArithmetic {}

public struct
_Abs {}

@_versioned
internal func _abs<Args>(_ args: Args) ->
(_Abs, Args) {}

// ./Math/Integers.swift

module Math
.
Integers

// _abs is visible in this module and all others within the project,
// but is not exported along with it.
internal import Math.Integers.Arithmetic

public protocol IntegerArithmetic : _IntegerArithmetic, Comparable
{}

public protocol SignedNumber : Comparable
, ExpressibleByIntegerLiteral {}

// Math.swift

module Math

// Exports the entire public contents of Math.Integers, but nothing in
// Math.Integers.Arithmetic.
public import Math.Integers
Modules names are tied to a directory structure that describes their location relative to the current module and it will now be an error to violate this rule. For example:

module String // lives in ./String.swift

module
String.Core // lives in ./String/Core.swift

module
String.Core.Internals.Do.You.Even.Write // lives in ./String/Core/Internals/Do/You/Even/Write.swift
Existing projects that do not adopt these rules will still retain their implicit module name (usually defined as the name of the framework or application that is being built) and may continue to use whatever directory structure they wish, however they may not declare any explicit modules.

This proposal also solves the problem of module export. A module that is imported without an access level modifier will default to an internal import per usual. However, when it is useful to fully expose the public content of submodules to a client, a public modifier can be used. Similarly, when it is useful to access internal or [file]private APIs, but not expose them to clients, those access modifiers may be used. The rule of thumb is: Only identifiers that are at least as visible as the qualifier on the import make for valid import declarations. For example:

// A submodule declaring a `private` class that gets imported with
// an `internal` qualifier with a `using` directive is an invalid import
// declaration.

module Foo
.
Bar

private class
PrivateThing {}

module Foo

// Error: PrivateThing not visible, use `private import`
import Foo.Bar using (PrivateThing)
// However, a submodule declaring a `public` struct that gets imported with
// an `private` qualifier is a valid import declaration.

module Foo
.
Bar

public class
PublicThing {}

module Foo

// All good! Foo can see Foo.Bar.PrivateThing.
private import Foo.Bar using (PublicThing)
Because import directives are file-local, they will never be exported along with a public import and will default to exporting the entire contents of the module as though you had never declared them.

// In this file and this file alone, the directives apply. To the user
// of this module, it is as though this declaration were simply:
// public import Foundation.Date
public import Foundation.Date hiding (Date.init
())
                              renaming (Date
.Type.
distantPast,
                                        to: Date
.Type.
letsGoLivingInThePast,
                                        Date
.Type.
timeIntervalSinceReferenceDate,
                                        to: Date
.Type.
startOfTheUniverse)
                              renaming (Date
.Type.<, to: Date.Type.<<<<<)
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. Code that is not organized into modules will remain unaffected and organized into one contiguous top-level module. However, it is strongly recommended that frameworks be decomposed and reorganized around the new module system.

As a case study, the public interface to the standard library appears to already be mostly broken down into submodules as described in GroupInfo.json.

Code that is defined in modulemaps already defines a module structure that can be imported directly into this scheme.

Alternatives considered

Module export can also be placed on the module declaration itself. The relevant parts of the grammar that have changed are below with an example:

module-decl -> <access-level-modifier> module <module-path>
import-decl -> import <module-path> <(opt) import-directive-list>

private module String.Core.
Internals

// Shh, it's a secret.
While this style makes it immediately obvious to the library author which modules are public or private, it causes the consumer problems because submodule exports are no longer explicit and are entirely ad-hoc. In the interest of enabling, for one, users of IDEs to drill into public submodules, making export local to import seems more appropriate.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Xiaodi Wu) #5

Hello all,

TJ Usiyan, Harlan Haskins, and I have been working on a proposal to rework
qualified imports and introduce an explicit module system to Swift that
we’d like to publish for your viewing pleasure.

The initial impetus was set out in a radar (rdar://17630570) I sent
fairly early on that didn’t receive a response, so I started a
swift-evolution
<http://permalink.gmane.org/gmane.comp.lang.swift.evolution/1378> thread
discussing the basics of this proposal. It has been refined and expanded a
bit to include an effort to make Swift modules explicit and updated with
the feedback of that first thread. Contents of the proposal are inline and can
also be had as a gist
<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6> or on
Github. <https://github.com/apple/swift-evolution/pull/440>

Cheers,

~Robert Widmann

Qualified Imports and Modules

   - Proposal: SE-NNNN
   <https://gist.github.com/CodaFi/NNNN-first-class-qualified-imports.md>
   - Authors: Robert Widmann <https://github.com/codafi>, Harlan Haskins
   <https://github.com/harlanhaskins>, 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 and the introduction of a module system.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#motivation>
Motivation

Swift code is modular by default. However, it is not clear how to
decompose existing modules further into submodules. In addition, it is
difficult to tell how importing a module affects its export to consumers of
a library. This leads many to either fake namespaces with enums, attempt to
structure Swift code with modulemaps, or use a large amount of
version-control submodules. All of these can be rolled into one complete
package in the form of a comprehensive rethink of the qualified import
system and the introduction of a module system.

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

Modules will now become an explicit part of working with canonical Swift
code. The grammar and semantics of qualified imports will change completely
with the addition of *import qualifiers* and *import directives*. We also
introduce three new contextual keywords: using, hiding, and renaming, 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

module-decl -> module <module-path>
import-decl -> <access-level-modifier> import <module-path> <(opt) import-directive-list>
module-path -> <identifier>
            -> <identifier>.<import-path>
import-directive-list -> <import-directive>
                      -> <import-directive> <import-directive-list>
import-directive -> using (<identifier>, ...)
                 -> hiding (<identifier>, ...)
                 -> renaming (<identifier>, to: <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 3 operations:

1) *using*: The *using* directive is followed by a list of identifiers
within the imported module that should be exposed to this file.

// The only visible parts of Foundation in this file are // Date.init(), Date.hashValue, and Date.description.import Foundation.Date using (Date.init(), Date.hashValue, Date.description)

2) *hiding*: The hiding directive is followed by a list of identifiers
within the imported module that should be hidden from this file.

// Imports all of Foundation.Date except `Date.compare()`import Foundation.Date hiding (Date.compare())

3) *renaming*: The renaming directive is followed by a list of
identifiers separated by to: that should be exposed to this file but
renamed.

// Imports all of Dispatch.DispatchQueue but renames the static member // DispatchQueue.main, to DispatchQueue.mainQueueimport Dispatch.DispatchQueue renaming (DispatchQueue.Type.main to: DispatchQueue.Type.mainQueue)// Renaming can also rename modules. All members of UIKit have to be qualified with// `UI` now.import UIKit renaming (UIKit, to: UI)

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

// Imports all of Foundation except `DateFormatter` and renames `Cache` to `LRUCache`import Foundation hiding (DateFormatter) renaming (Cache to: LRUCache)// Imports SCNNode except SCNNode.init(mdlObject:) and renames `.description` to// `.nodeDescription` import SceneKit using (SCNNode)
                renaming (SCNNode.description, to: SCNNode.nodeDescription)
                hiding (SCNNode.init(mdlObject:))

Directive chaining occurs left-to-right:

// This says to 1) Hide nothing 2) Use nothing 3) rename Int to INT. It is invalid// because 1) We will show everything 2) Then hide everything 3) Therefore Int is unavailable, error.import Swift hiding () using () renaming (Int, to: INT)// 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. 3) Double is unavailable, error.import Swift using (Int) hiding (String) renaming (Double, to: Triple)// 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)

I don't mean to detract from the conversation on the deeper design aspects
of this proposal, but in terms of the proposed syntax I think there's
opportunity for simplification. I understand the desire to use parentheses,
especially to avoid taking up `to` even as a contextual keyword, but I
think it is possible to improve clarity by way of some tweaks:

import Swift using Int

import Swift using Int as INT, Double as Triple
// there is no possible confusion with casting here,
// and the use of `as` in this context would be familiar
// for those coming from some other languages

import Swift using Int, Double as Triple, String as _
// poof, no more String
···

On Mon, Jul 18, 2016 at 4:09 PM, Robert Widmann via swift-evolution < swift-evolution@swift.org> wrote:

Module scope is delimited by the keyword module followed by a fully
qualified name and must occur as the first declaration in a file. For
example:

// ./Math/Integers/Arithmetic.swift
module Math.Integers.Arithmetic
public protocol _IntegerArithmetic {}
public struct _Abs {}
@_versionedinternal func _abs<Args>(_ args: Args) -> (_Abs, Args) {}
// ./Math/Integers.swift
module Math.Integers
// _abs is visible in this module and all others within the project, // but is not exported along with it.internal import Math.Integers.Arithmetic
public protocol IntegerArithmetic : _IntegerArithmetic, Comparable {}public protocol SignedNumber : Comparable, ExpressibleByIntegerLiteral {}

// Math.swift
module Math
// Exports the entire public contents of Math.Integers, but nothing in // Math.Integers.Arithmetic.public import Math.Integers

Modules names are tied to a directory structure that describes their
location relative to the current module and it will now be an error to
violate this rule. For example:

module String // lives in ./String.swift
module String.Core // lives in ./String/Core.swift
module String.Core.Internals.Do.You.Even.Write // lives in ./String/Core/Internals/Do/You/Even/Write.swift

Existing projects that do not adopt these rules will still retain their *implicit
module name* (usually defined as the name of the framework or application
that is being built) and may continue to use whatever directory structure
they wish, however they may not declare any explicit modules.

This proposal also solves the problem of module *export*. A module that
is imported without an access level modifier will default to an internal import
per usual. However, when it is useful to fully expose the public content of
submodules to a client, a public modifier can be used. Similarly, when it
is useful to access internal or [file]private APIs, but not expose them
to clients, those access modifiers may be used. The rule of thumb is: Only
identifiers that are at least as visible as the qualifier on the import
make for valid import declarations. For example:

// A submodule declaring a `private` class that gets imported with // an `internal` qualifier with a `using` directive is an invalid import // declaration.
module Foo.Bar
private class PrivateThing {}

module Foo
// Error: PrivateThing not visible, use `private import`import Foo.Bar using (PrivateThing)

// However, a submodule declaring a `public` struct that gets imported with // an `private` qualifier is a valid import declaration.
module Foo.Bar
public class PublicThing {}

module Foo
// All good! Foo can see Foo.Bar.PrivateThing.private import Foo.Bar using (PublicThing)

Because import directives are file-local, they will never be exported
along with a public import and will default to exporting the entire
contents of the module as though you had never declared them.

// In this file and this file alone, the directives apply. To the user// of this module, it is as though this declaration were simply:// public import Foundation.Datepublic import Foundation.Date hiding (Date.init())
                              renaming (Date.Type.distantPast,
                                        to: Date.Type.letsGoLivingInThePast,
                                        Date.Type.timeIntervalSinceReferenceDate,
                                        to: Date.Type.startOfTheUniverse)
                              renaming (Date.Type.<, to: Date.Type.<<<<<)

<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. Code that is not organized into modules will remain
unaffected and organized into one contiguous top-level module. However, it
is strongly recommended that frameworks be decomposed and reorganized
around the new module system.

As a case study, the public interface to the standard library appears to
already be mostly broken down into submodules as described in
GroupInfo.json
<https://github.com/apple/swift/blob/master/stdlib/public/core/GroupInfo.json>
.

Code that is defined in modulemaps already defines a module structure that
can be imported directly into this scheme.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#alternatives-considered>Alternatives
considered

Module export can also be placed on the module declaration itself. The
relevant parts of the grammar that have changed are below with an example:

module-decl -> <access-level-modifier> module <module-path>
import-decl -> import <module-path> <(opt) import-directive-list>

private module String.Core.Internals
// Shh, it's a secret.

While this style makes it immediately obvious to the library author which
modules are public or private, it causes the consumer problems because
submodule exports are no longer explicit and are entirely ad-hoc. In the
interest of enabling, for one, users of IDEs to drill into public
submodules, making export local to import seems more appropriate.

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


(Xiaodi Wu) #6

This is an interesting document. I think it deserves careful study. For
now, some questions:

What is the rationale behind permitting the using of specific methods? This
seems to be usually fine-grained in comparison to other languages. What use
cases do you have in mind for this?

I can see the use case for hiding specific symbols when they come into
conflict with your own, but in your example you're hiding specific methods
declared *in* an imported type. What is the use case here? Is it going to
allow me to open backdoors so that, if I don't like `Foo.frobnicate()`, I
can hide it and then substitute my own in an extension? This seems like a
bad thing at first blush.

I can see the obvious use case for renaming modules and types on
import--basically, in my mind, it's like typealiases with hiding, and it's
available in other languages of course. But how would renaming methods
work? If Foo conforms to Equatable and I rename `Foo.==` to `Foo.!=`, is
the type I import still Equatable? How would it behave? And even if Foo is
fine, what happens if I try to subclass my Frankensteinian Foo?

···

On Mon, Jul 18, 2016 at 16:10 Robert Widmann via swift-evolution < swift-evolution@swift.org> wrote:

Hello all,

TJ Usiyan, Harlan Haskins, and I have been working on a proposal to rework
qualified imports and introduce an explicit module system to Swift that
we’d like to publish for your viewing pleasure.

The initial impetus was set out in a radar (rdar://17630570) I sent
fairly early on that didn’t receive a response, so I started a
swift-evolution
<http://permalink.gmane.org/gmane.comp.lang.swift.evolution/1378> thread
discussing the basics of this proposal. It has been refined and expanded a
bit to include an effort to make Swift modules explicit and updated with
the feedback of that first thread. Contents of the proposal are inline and can
also be had as a gist
<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6> or on
Github. <https://github.com/apple/swift-evolution/pull/440>

Cheers,

~Robert Widmann

Qualified Imports and Modules

   - Proposal: SE-NNNN
   <https://gist.github.com/CodaFi/NNNN-first-class-qualified-imports.md>
   - Authors: Robert Widmann <https://github.com/codafi>, Harlan Haskins
   <https://github.com/harlanhaskins>, 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 and the introduction of a module system.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#motivation>
Motivation

Swift code is modular by default. However, it is not clear how to
decompose existing modules further into submodules. In addition, it is
difficult to tell how importing a module affects its export to consumers of
a library. This leads many to either fake namespaces with enums, attempt to
structure Swift code with modulemaps, or use a large amount of
version-control submodules. All of these can be rolled into one complete
package in the form of a comprehensive rethink of the qualified import
system and the introduction of a module system.

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

Modules will now become an explicit part of working with canonical Swift
code. The grammar and semantics of qualified imports will change completely
with the addition of *import qualifiers* and *import directives*. We also
introduce three new contextual keywords: using, hiding, and renaming, 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

module-decl -> module <module-path>
import-decl -> <access-level-modifier> import <module-path> <(opt) import-directive-list>
module-path -> <identifier>
            -> <identifier>.<import-path>
import-directive-list -> <import-directive>
                      -> <import-directive> <import-directive-list>
import-directive -> using (<identifier>, ...)
                 -> hiding (<identifier>, ...)
                 -> renaming (<identifier>, to: <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 3 operations:

1) *using*: The *using* directive is followed by a list of identifiers
within the imported module that should be exposed to this file.

// The only visible parts of Foundation in this file are // Date.init(), Date.hashValue, and Date.description.import Foundation.Date using (Date.init(), Date.hashValue, Date.description)

2) *hiding*: The hiding directive is followed by a list of identifiers
within the imported module that should be hidden from this file.

// Imports all of Foundation.Date except `Date.compare()`import Foundation.Date hiding (Date.compare())

3) *renaming*: The renaming directive is followed by a list of
identifiers separated by to: that should be exposed to this file but
renamed.

// Imports all of Dispatch.DispatchQueue but renames the static member // DispatchQueue.main, to DispatchQueue.mainQueueimport Dispatch.DispatchQueue renaming (DispatchQueue.Type.main to: DispatchQueue.Type.mainQueue)// Renaming can also rename modules. All members of UIKit have to be qualified with// `UI` now.import UIKit renaming (UIKit, to: UI)

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

// Imports all of Foundation except `DateFormatter` and renames `Cache` to `LRUCache`import Foundation hiding (DateFormatter) renaming (Cache to: LRUCache)// Imports SCNNode except SCNNode.init(mdlObject:) and renames `.description` to// `.nodeDescription` import SceneKit using (SCNNode)
                renaming (SCNNode.description, to: SCNNode.nodeDescription)
                hiding (SCNNode.init(mdlObject:))

Directive chaining occurs left-to-right:

// This says to 1) Hide nothing 2) Use nothing 3) rename Int to INT. It is invalid// because 1) We will show everything 2) Then hide everything 3) Therefore Int is unavailable, error.import Swift hiding () using () renaming (Int, to: INT)// 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. 3) Double is unavailable, error.import Swift using (Int) hiding (String) renaming (Double, to: Triple)// 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)

Module scope is delimited by the keyword module followed by a fully
qualified name and must occur as the first declaration in a file. For
example:

// ./Math/Integers/Arithmetic.swift
module Math.Integers.Arithmetic
public protocol _IntegerArithmetic {}
public struct _Abs {}
@_versionedinternal func _abs<Args>(_ args: Args) -> (_Abs, Args) {}
// ./Math/Integers.swift
module Math.Integers
// _abs is visible in this module and all others within the project, // but is not exported along with it.internal import Math.Integers.Arithmetic
public protocol IntegerArithmetic : _IntegerArithmetic, Comparable {}public protocol SignedNumber : Comparable, ExpressibleByIntegerLiteral {}

// Math.swift
module Math
// Exports the entire public contents of Math.Integers, but nothing in // Math.Integers.Arithmetic.public import Math.Integers

Modules names are tied to a directory structure that describes their
location relative to the current module and it will now be an error to
violate this rule. For example:

module String // lives in ./String.swift
module String.Core // lives in ./String/Core.swift
module String.Core.Internals.Do.You.Even.Write // lives in ./String/Core/Internals/Do/You/Even/Write.swift

Existing projects that do not adopt these rules will still retain their *implicit
module name* (usually defined as the name of the framework or application
that is being built) and may continue to use whatever directory structure
they wish, however they may not declare any explicit modules.

This proposal also solves the problem of module *export*. A module that
is imported without an access level modifier will default to an internal import
per usual. However, when it is useful to fully expose the public content of
submodules to a client, a public modifier can be used. Similarly, when it
is useful to access internal or [file]private APIs, but not expose them
to clients, those access modifiers may be used. The rule of thumb is: Only
identifiers that are at least as visible as the qualifier on the import
make for valid import declarations. For example:

// A submodule declaring a `private` class that gets imported with // an `internal` qualifier with a `using` directive is an invalid import // declaration.
module Foo.Bar
private class PrivateThing {}

module Foo
// Error: PrivateThing not visible, use `private import`import Foo.Bar using (PrivateThing)

// However, a submodule declaring a `public` struct that gets imported with // an `private` qualifier is a valid import declaration.
module Foo.Bar
public class PublicThing {}

module Foo
// All good! Foo can see Foo.Bar.PrivateThing.private import Foo.Bar using (PublicThing)

Because import directives are file-local, they will never be exported
along with a public import and will default to exporting the entire
contents of the module as though you had never declared them.

// In this file and this file alone, the directives apply. To the user// of this module, it is as though this declaration were simply:// public import Foundation.Datepublic import Foundation.Date hiding (Date.init())
                              renaming (Date.Type.distantPast,
                                        to: Date.Type.letsGoLivingInThePast,
                                        Date.Type.timeIntervalSinceReferenceDate,
                                        to: Date.Type.startOfTheUniverse)
                              renaming (Date.Type.<, to: Date.Type.<<<<<)

<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. Code that is not organized into modules will remain
unaffected and organized into one contiguous top-level module. However, it
is strongly recommended that frameworks be decomposed and reorganized
around the new module system.

As a case study, the public interface to the standard library appears to
already be mostly broken down into submodules as described in
GroupInfo.json
<https://github.com/apple/swift/blob/master/stdlib/public/core/GroupInfo.json>
.

Code that is defined in modulemaps already defines a module structure that
can be imported directly into this scheme.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#alternatives-considered>Alternatives
considered

Module export can also be placed on the module declaration itself. The
relevant parts of the grammar that have changed are below with an example:

module-decl -> <access-level-modifier> module <module-path>
import-decl -> import <module-path> <(opt) import-directive-list>

private module String.Core.Internals
// Shh, it's a secret.

While this style makes it immediately obvious to the library author which
modules are public or private, it causes the consumer problems because
submodule exports are no longer explicit and are entirely ad-hoc. In the
interest of enabling, for one, users of IDEs to drill into public
submodules, making export local to import seems more appropriate.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(L Mihalkovic) #7

Interesting... mix of c# and swift and others

I am not sure it has many chances to see the light: unless they internally manage to cheat and reuse the natural scoping provided by empty enums, the implications on the binary structure of the dylibs are kolossal... and as if this was not enough, there will be a similar impact on the linux runtime. If memory serves, the last issues standing in the way for a windows port were just recently overcome (same business of how to store the runtime data into the structure of a dll), and this would send them back a few steps back.
Considering this is v3.0, the last of the breakers, and this is the very end of the cycle, with features still not designed as well as designs without coders, I think that a deep change like this one is rather unlikely for 3, and unless there is a clever way to make it additive in 4, then ever. Had swiftc been written in swift 4 years ago, this is likely one of the first things the core team would have missed, to deal with a 150,000+ loc codebase (i think in wwdc someone threw 500k as the total size today - the typescript type checker is ~20kloc).
But i still hope some variations on the theme will show up (preferably as somthing like the less verbose "using xxxx as vvvvv" "using xxxxx omitting bbbbbb" "using (bbbbb) from xxxxx")

Regards
(From mobile)

···

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

Hello all,

TJ Usiyan, Harlan Haskins, and I have been working on a proposal to rework qualified imports and introduce an explicit module system to Swift that we’d like to publish for your viewing pleasure.

The initial impetus was set out in a radar (rdar://17630570) I sent fairly early on that didn’t receive a response, so I started a swift-evolution thread discussing the basics of this proposal. It has been refined and expanded a bit to include an effort to make Swift modules explicit and updated with the feedback of that first thread. Contents of the proposal are inline and can also be had as a gist or on Github.

Cheers,

~Robert Widmann

Qualified Imports and Modules

Proposal: SE-NNNN
Authors: Robert Widmann, Harlan Haskins, TJ Usiyan
Status: Awaiting review
Review manager: TBD
Introduction

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

Motivation

Swift code is modular by default. However, it is not clear how to decompose existing modules further into submodules. In addition, it is difficult to tell how importing a module affects its export to consumers of a library. This leads many to either fake namespaces with enums, attempt to structure Swift code with modulemaps, or use a large amount of version-control submodules. All of these can be rolled into one complete package in the form of a comprehensive rethink of the qualified import system and the introduction of a module system.

Proposed solution

Modules will now become an explicit part of working with canonical Swift code. The grammar and semantics of qualified imports will change completely with the addition of import qualifiers and import directives. We also introduce three new contextual keywords: using, hiding, and renaming, to facilitate fine-grained usage of module contents.

Detailed design

Qualified import syntax will be revised to the following

module-decl -> module <module-path>
import-decl -> <access-level-modifier> import <module-path> <(opt) import-directive-list>
module-path -> <identifier>
            -> <identifier>.<import-path>
import-directive-list -> <import-directive>
                      -> <import-directive> <import-directive-list>
import-directive -> using (<identifier>, ...)
                 -> hiding (<identifier>, ...)
                 -> renaming (<identifier>, to: <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 3 operations:

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

// The only visible parts of Foundation in this file are
// Date.init(), Date.hashValue, and Date.description.
import Foundation.Date using (Date.init(), Date.hashValue, Date.description)
2) hiding: The hiding directive is followed by a list of identifiers within the imported module that should be hidden from this file.

// Imports all of Foundation.Date except `Date.compare()`
import Foundation.Date hiding (Date.compare())
3) renaming: The renaming directive is followed by a list of identifiers separated by to: that should be exposed to this file but renamed.

// Imports all of Dispatch.DispatchQueue but renames the static member
// DispatchQueue.main, to DispatchQueue.mainQueue
import Dispatch.DispatchQueue renaming (DispatchQueue.Type.main to: DispatchQueue.Type.mainQueue)
// Renaming can also rename modules. All members of UIKit have to be qualified with
// `UI` now.
import UIKit renaming (UIKit, to: UI)
Import directives chain to one another and can be used to create a fine-grained module import:

// Imports all of Foundation except `DateFormatter` and renames `Cache` to `LRUCache`
import Foundation hiding (DateFormatter) renaming (Cache to: LRUCache)
// Imports SCNNode except SCNNode.init(mdlObject:) and renames `.description` to
// `.nodeDescription`
import SceneKit using (SCNNode)
                renaming (SCNNode.description, to: SCNNode.nodeDescription)
                hiding (SCNNode.init(mdlObject:))
Directive chaining occurs left-to-right:

// This says to 1) Hide nothing 2) Use nothing 3) rename Int to INT. It is invalid
// because 1) We will show everything 2) Then hide everything 3) Therefore Int is unavailable, error.
import Swift hiding () using () renaming (Int, to: INT)
// 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. 3) Double is unavailable, error.
import Swift using (Int) hiding (String) renaming (Double, to: Triple)
// 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)
Module scope is delimited by the keyword module followed by a fully qualified name and must occur as the first declaration in a file. For example:

// ./Math/Integers/Arithmetic.swift
module Math.Integers.Arithmetic

public protocol _IntegerArithmetic {}

public struct _Abs {}

@_versioned
internal func _abs<Args>(_ args: Args) -> (_Abs, Args) {}

// ./Math/Integers.swift
module Math.Integers

// _abs is visible in this module and all others within the project,
// but is not exported along with it.
internal import Math.Integers.Arithmetic

public protocol IntegerArithmetic : _IntegerArithmetic, Comparable {}
public protocol SignedNumber : Comparable, ExpressibleByIntegerLiteral {}

// Math.swift
module Math

// Exports the entire public contents of Math.Integers, but nothing in
// Math.Integers.Arithmetic.
public import Math.Integers
Modules names are tied to a directory structure that describes their location relative to the current module and it will now be an error to violate this rule. For example:

module String // lives in ./String.swift
module String.Core // lives in ./String/Core.swift
module String.Core.Internals.Do.You.Even.Write // lives in ./String/Core/Internals/Do/You/Even/Write.swift
Existing projects that do not adopt these rules will still retain their implicit module name (usually defined as the name of the framework or application that is being built) and may continue to use whatever directory structure they wish, however they may not declare any explicit modules.

This proposal also solves the problem of module export. A module that is imported without an access level modifier will default to an internal import per usual. However, when it is useful to fully expose the public content of submodules to a client, a public modifier can be used. Similarly, when it is useful to access internal or [file]private APIs, but not expose them to clients, those access modifiers may be used. The rule of thumb is: Only identifiers that are at least as visible as the qualifier on the import make for valid import declarations. For example:

// A submodule declaring a `private` class that gets imported with
// an `internal` qualifier with a `using` directive is an invalid import
// declaration.
module Foo.Bar

private class PrivateThing {}

module Foo

// Error: PrivateThing not visible, use `private import`
import Foo.Bar using (PrivateThing)
// However, a submodule declaring a `public` struct that gets imported with
// an `private` qualifier is a valid import declaration.
module Foo.Bar

public class PublicThing {}

module Foo

// All good! Foo can see Foo.Bar.PrivateThing.
private import Foo.Bar using (PublicThing)
Because import directives are file-local, they will never be exported along with a public import and will default to exporting the entire contents of the module as though you had never declared them.

// In this file and this file alone, the directives apply. To the user
// of this module, it is as though this declaration were simply:
// public import Foundation.Date
public import Foundation.Date hiding (Date.init())
                              renaming (Date.Type.distantPast,
                                        to: Date.Type.letsGoLivingInThePast,
                                        Date.Type.timeIntervalSinceReferenceDate,
                                        to: Date.Type.startOfTheUniverse)
                              renaming (Date.Type.<, to: Date.Type.<<<<<)
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. Code that is not organized into modules will remain unaffected and organized into one contiguous top-level module. However, it is strongly recommended that frameworks be decomposed and reorganized around the new module system.

As a case study, the public interface to the standard library appears to already be mostly broken down into submodules as described in GroupInfo.json.

Code that is defined in modulemaps already defines a module structure that can be imported directly into this scheme.

Alternatives considered

Module export can also be placed on the module declaration itself. The relevant parts of the grammar that have changed are below with an example:

module-decl -> <access-level-modifier> module <module-path>
import-decl -> import <module-path> <(opt) import-directive-list>
private module String.Core.Internals

// Shh, it's a secret.
While this style makes it immediately obvious to the library author which modules are public or private, it causes the consumer problems because submodule exports are no longer explicit and are entirely ad-hoc. In the interest of enabling, for one, users of IDEs to drill into public submodules, making export local to import seems more appropriate.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Karl) #8

I’m not a fan of renaming. Along with ‘dialects’ of the Swift language itself, allowing people to rename APIs (because they don’t like Apple’s long names) makes it harder for everybody else to understand.

I know people who do this at the moment with typedefs/typealiases. Drives me nuts. Especially if it’s per-file; oh God, can you imagine? Having to re-learn a new set of SDK mappings when you open a new file?

I also don’t understand the argument about free-functions “polluting the global namespace” (I’ve heard it a couple of times). You can still get qualified access to free functions (e.g. Swift.repeatElement(…)), so if there is a conflict you can resolve it like you do any other conflict - by qualifying it with the module name.

Karl

···

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

Hello all,

TJ Usiyan, Harlan Haskins, and I have been working on a proposal to rework qualified imports and introduce an explicit module system to Swift that we’d like to publish for your viewing pleasure.

The initial impetus was set out in a radar (rdar://17630570 <rdar://17630570>) I sent fairly early on that didn’t receive a response, so I started a swift-evolution <http://permalink.gmane.org/gmane.comp.lang.swift.evolution/1378> thread discussing the basics of this proposal. It has been refined and expanded a bit to include an effort to make Swift modules explicit and updated with the feedback of that first thread. Contents of the proposal are inline and can also be had as a gist <https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6> or on Github. <https://github.com/apple/swift-evolution/pull/440>

Cheers,

~Robert Widmann

Qualified Imports and Modules

Proposal: SE-NNNN <https://gist.github.com/CodaFi/NNNN-first-class-qualified-imports.md>
Authors: Robert Widmann <https://github.com/codafi>, Harlan Haskins <https://github.com/harlanhaskins>, 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 and the introduction of a module system.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#motivation>Motivation

Swift code is modular by default. However, it is not clear how to decompose existing modules further into submodules. In addition, it is difficult to tell how importing a module affects its export to consumers of a library. This leads many to either fake namespaces with enums, attempt to structure Swift code with modulemaps, or use a large amount of version-control submodules. All of these can be rolled into one complete package in the form of a comprehensive rethink of the qualified import system and the introduction of a module system.

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

Modules will now become an explicit part of working with canonical Swift code. The grammar and semantics of qualified imports will change completely with the addition of import qualifiers and import directives. We also introduce three new contextual keywords: using, hiding, and renaming, 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

module-decl -> module <module-path>
import-decl -> <access-level-modifier> import <module-path> <(opt) import-directive-list>
module-path -> <identifier>
            -> <identifier>.<import-path>
import-directive-list -> <import-directive>
                      -> <import-directive> <import-directive-list>
import-directive -> using (<identifier>, ...)
                 -> hiding (<identifier>, ...)
                 -> renaming (<identifier>, to: <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 3 operations:

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

// The only visible parts of Foundation in this file are
// Date.init(), Date.hashValue, and Date.description.
import Foundation.Date using (Date.init(), Date.hashValue, Date.description)
2) hiding: The hiding directive is followed by a list of identifiers within the imported module that should be hidden from this file.

// Imports all of Foundation.Date except `Date.compare()`
import Foundation.Date hiding (Date.compare())
3) renaming: The renaming directive is followed by a list of identifiers separated by to: that should be exposed to this file but renamed.

// Imports all of Dispatch.DispatchQueue but renames the static member
// DispatchQueue.main, to DispatchQueue.mainQueue
import Dispatch.DispatchQueue renaming (DispatchQueue.Type.main to: DispatchQueue.Type.mainQueue)
// Renaming can also rename modules. All members of UIKit have to be qualified with
// `UI` now.
import UIKit renaming (UIKit, to: UI)
Import directives chain to one another and can be used to create a fine-grained module import:

// Imports all of Foundation except `DateFormatter` and renames `Cache` to `LRUCache`
import Foundation hiding (DateFormatter) renaming (Cache to: LRUCache)
// Imports SCNNode except SCNNode.init(mdlObject:) and renames `.description` to
// `.nodeDescription`
import SceneKit using (SCNNode)
                renaming (SCNNode.description, to: SCNNode.nodeDescription)
                hiding (SCNNode.init(mdlObject:))
Directive chaining occurs left-to-right:

// This says to 1) Hide nothing 2) Use nothing 3) rename Int to INT. It is invalid
// because 1) We will show everything 2) Then hide everything 3) Therefore Int is unavailable, error.
import Swift hiding () using () renaming (Int, to: INT)
// 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. 3) Double is unavailable, error.
import Swift using (Int) hiding (String) renaming (Double, to: Triple)
// 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)
Module scope is delimited by the keyword module followed by a fully qualified name and must occur as the first declaration in a file. For example:

// ./Math/Integers/Arithmetic.swift
module Math.Integers.Arithmetic

public protocol _IntegerArithmetic {}

public struct _Abs {}

@_versioned
internal func _abs<Args>(_ args: Args) -> (_Abs, Args) {}

// ./Math/Integers.swift
module Math.Integers

// _abs is visible in this module and all others within the project,
// but is not exported along with it.
internal import Math.Integers.Arithmetic

public protocol IntegerArithmetic : _IntegerArithmetic, Comparable {}
public protocol SignedNumber : Comparable, ExpressibleByIntegerLiteral {}

// Math.swift
module Math

// Exports the entire public contents of Math.Integers, but nothing in
// Math.Integers.Arithmetic.
public import Math.Integers
Modules names are tied to a directory structure that describes their location relative to the current module and it will now be an error to violate this rule. For example:

module String // lives in ./String.swift
module String.Core // lives in ./String/Core.swift
module String.Core.Internals.Do.You.Even.Write // lives in ./String/Core/Internals/Do/You/Even/Write.swift
Existing projects that do not adopt these rules will still retain their implicit module name (usually defined as the name of the framework or application that is being built) and may continue to use whatever directory structure they wish, however they may not declare any explicit modules.

This proposal also solves the problem of module export. A module that is imported without an access level modifier will default to an internal import per usual. However, when it is useful to fully expose the public content of submodules to a client, a public modifier can be used. Similarly, when it is useful to access internal or [file]private APIs, but not expose them to clients, those access modifiers may be used. The rule of thumb is: Only identifiers that are at least as visible as the qualifier on the import make for valid import declarations. For example:

// A submodule declaring a `private` class that gets imported with
// an `internal` qualifier with a `using` directive is an invalid import
// declaration.
module Foo.Bar

private class PrivateThing {}

module Foo

// Error: PrivateThing not visible, use `private import`
import Foo.Bar using (PrivateThing)
// However, a submodule declaring a `public` struct that gets imported with
// an `private` qualifier is a valid import declaration.
module Foo.Bar

public class PublicThing {}

module Foo

// All good! Foo can see Foo.Bar.PrivateThing.
private import Foo.Bar using (PublicThing)
Because import directives are file-local, they will never be exported along with a public import and will default to exporting the entire contents of the module as though you had never declared them.

// In this file and this file alone, the directives apply. To the user
// of this module, it is as though this declaration were simply:
// public import Foundation.Date
public import Foundation.Date hiding (Date.init())
                              renaming (Date.Type.distantPast,
                                        to: Date.Type.letsGoLivingInThePast,
                                        Date.Type.timeIntervalSinceReferenceDate,
                                        to: Date.Type.startOfTheUniverse)
                              renaming (Date.Type.<, to: Date.Type.<<<<<)
<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. Code that is not organized into modules will remain unaffected and organized into one contiguous top-level module. However, it is strongly recommended that frameworks be decomposed and reorganized around the new module system.

As a case study, the public interface to the standard library appears to already be mostly broken down into submodules as described in GroupInfo.json <https://github.com/apple/swift/blob/master/stdlib/public/core/GroupInfo.json>.

Code that is defined in modulemaps already defines a module structure that can be imported directly into this scheme.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#alternatives-considered>Alternatives considered

Module export can also be placed on the module declaration itself. The relevant parts of the grammar that have changed are below with an example:

module-decl -> <access-level-modifier> module <module-path>
import-decl -> import <module-path> <(opt) import-directive-list>
private module String.Core.Internals

// Shh, it's a secret.
While this style makes it immediately obvious to the library author which modules are public or private, it causes the consumer problems because submodule exports are no longer explicit and are entirely ad-hoc. In the interest of enabling, for one, users of IDEs to drill into public submodules, making export local to import seems more appropriate.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Pyry Jahkola) #9

Firstly, thank you Robert, TJ, and Harlan for the proposal and especially for considering the introduction of submodules too! Those would be very useful when Swift libraries start growing. But maybe we can delay that discussion past Swift 3.

* * *

Secondly, I used to be in the selective import camp before, but I think it's somewhat counter to Swift's feel and direction where most of the API is wrapped inside types, and function names consist of not just the base name but argument labels too (even if we now have the "Gregorian" naming SE-0021 available). So while I don't see a big problem introducing something like

    import Foo.(fooConstant, bar(a:b) as bar1(a:b:), IrrelevantType as _)

to pick identifiers à la carte, I don't think it would be useful very often. And the alternative of accessing `bar` through the module name as `Foo.bar` does work just as well already.

…Well, that is except for the problem when the module name `Foo` happens to be shadowed by a non-module name `Foo`. I'd rather tackle that problem, and I do so as follows:

* * *

My usual pain points with imported (micro-)frameworks have been:

A) …when two modules (e.g. `Foo` and `Bar`) export the same identifier (`baz`) for different meanings.
B) …when a local or imported identifier (e.g. `class Tree`) has the same name as a module `Tree`.

In case A, I can just disambiguate by prefixing with the module name: `Foo.baz` vs. `Bar.baz`.

In case B, there is really no way around that I'm aware of. Anyway you write `Tree.foo` Swift will try to look up under the type `Tree` and never the module `Tree`.

Those two issues could be addressed by simply introducing qualified imports as follows:

    import Darwin as Darwin // qualified import (there could be a shorthand but what?)
    import UIKit as UI // qualified import with renaming
    import Tree // unqualified import brings in struct Tree.Tree, among others.
    import Tree as T // qualified import
    
    let out = Darwin.stdout // ok
    var vc: UI.UIViewController? // ok
    let tree: Tree = T.emptyTree // ok; `struct Tree.Tree` was imported unqualified
    
    let err = stderr
    // error: Use of unresolved identifier 'stderr'
    // note: did you mean 'Darwin.stderr'?
    
    var view: UIView?
    // error: Use of unresolved type 'UIView'
    // note: did you mean 'UI.UIView'?
    
    enum UI {}
    // error: invalid redeclaration of 'UI'
    // note: 'UI' previously declared (line 2)

The qualified import syntax, `import MODULE as NAME`, imports the module MODULE such that its contents can be accessed through the prefix of `NAME.` – but not without it, unless also imported without qualification.

The given NAME uses up that identifier in the current scope (as if it was a private type) such that there can be no locally visible type or value with the same name in that scope. For example, if the current module defined NAME publicly or internally in another file, then that identifier would be shadowed in this file by the qualified module name NAME.

Side note: It is still possible to chain multiple module imports, qualified or not, on the same line (albeit not a particularly good coding style if they're unrelated). The order of imports does not matter. So the above example could've been written as:

    import Darwin as Darwin, UIKit as UI, Tree as T, Tree

* * *

I think that's the small change we need most urgently. The rest can wait.

— Pyry

PS: Another thing I'd see useful, especially when migrating code from the `MyPlayground_Sources` module to a real project would be a syntax something like `_.foo` to access the `foo` that is visible in the current file top-level, so to escape any local shadowing.

···

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

TJ Usiyan, Harlan Haskins, and I have been working on a proposal to rework qualified imports and introduce an explicit module system to Swift that we’d like to publish for your viewing pleasure.

The initial impetus was set out in a radar (rdar://17630570 <rdar://17630570>) I sent fairly early on that didn’t receive a response, so I started a swift-evolution <http://permalink.gmane.org/gmane.comp.lang.swift.evolution/1378> thread discussing the basics of this proposal. It has been refined and expanded a bit to include an effort to make Swift modules explicit and updated with the feedback of that first thread. Contents of the proposal are inline and can also be had as a gist <https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6> or on Github. <https://github.com/apple/swift-evolution/pull/440>


(Beta) #10

This is an interesting document. I think it deserves careful study. For now, some questions:

What is the rationale behind permitting the using of specific methods? This seems to be usually fine-grained in comparison to other languages. What use cases do you have in mind for this?

One use case: Swift libraries export not just member references as I’ve used here, but a large amount of free functions. It has long been a problem that free functions seem to pollute a shared namespace and there didn’t seem to be a clear way to hide them.

I can see the use case for hiding specific symbols when they come into conflict with your own, but in your example you're hiding specific methods declared *in* an imported type. What is the use case here? Is it going to allow me to open backdoors so that, if I don't like `Foo.frobnicate()`, I can hide it and then substitute my own in an extension? This seems like a bad thing at first blush.

For members that would be an acceptable use-case. The worst-case scenario that comes to mind is this being used as a way to “virtually override” a method in a subclass. Then again, the scope of the damage is limited to the file in which you’ve declared this monstrosity so clients and even you will not be able to see it outside of there unless you explicitly redeclare the hiding import (in which case, you probably know what you’re doing).

A use care here might be hiding the KVO-ish parts of an object from yourself, or more generally subsetting out the part of an API you know you shouldn’t interact with in a particular submodule.

I can see the obvious use case for renaming modules and types on import--basically, in my mind, it's like typealiases with hiding, and it's available in other languages of course. But how would renaming methods work? If Foo conforms to Equatable and I rename `Foo.==` to `Foo.!=`, is the type I import still Equatable? How would it behave? And even if Foo is fine, what happens if I try to subclass my Frankensteinian Foo?

Of course you still conform to Equatable. The renaming defines a mapping from your names to “proper" names. For example, if you use a renaming import to change the requirements of a protocol in a file, then your conformance will simply look at the mapping and see that everything resolves into its proper place. Bear in mind that your renamings will not survive outside of the file in which you declare them. Frankenteinian Foo exists where you say it does and nowhere else. Everybody else just sees Foo conform to Equatable (unless they rename things themselves).

···

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

On Mon, Jul 18, 2016 at 16:10 Robert Widmann via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Hello all,

TJ Usiyan, Harlan Haskins, and I have been working on a proposal to rework qualified imports and introduce an explicit module system to Swift that we’d like to publish for your viewing pleasure.

The initial impetus was set out in a radar (rdar://17630570 <>) I sent fairly early on that didn’t receive a response, so I started a swift-evolution <http://permalink.gmane.org/gmane.comp.lang.swift.evolution/1378> thread discussing the basics of this proposal. It has been refined and expanded a bit to include an effort to make Swift modules explicit and updated with the feedback of that first thread. Contents of the proposal are inline and can also be had as a gist <https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6> or on Github. <https://github.com/apple/swift-evolution/pull/440>

Cheers,

~Robert Widmann

Qualified Imports and Modules

Proposal: SE-NNNN <https://gist.github.com/CodaFi/NNNN-first-class-qualified-imports.md>
Authors: Robert Widmann <https://github.com/codafi>, Harlan Haskins <https://github.com/harlanhaskins>, 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 and the introduction of a module system.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#motivation>Motivation

Swift code is modular by default. However, it is not clear how to decompose existing modules further into submodules. In addition, it is difficult to tell how importing a module affects its export to consumers of a library. This leads many to either fake namespaces with enums, attempt to structure Swift code with modulemaps, or use a large amount of version-control submodules. All of these can be rolled into one complete package in the form of a comprehensive rethink of the qualified import system and the introduction of a module system.

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

Modules will now become an explicit part of working with canonical Swift code. The grammar and semantics of qualified imports will change completely with the addition of import qualifiers and import directives. We also introduce three new contextual keywords: using, hiding, and renaming, 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

module-decl -> module <module-path>
import-decl -> <access-level-modifier> import <module-path> <(opt) import-directive-list>
module-path -> <identifier>
            -> <identifier>.<import-path>
import-directive-list -> <import-directive>
                      -> <import-directive> <import-directive-list>
import-directive -> using (<identifier>, ...)
                 -> hiding (<identifier>, ...)
                 -> renaming (<identifier>, to: <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 3 operations:

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

// The only visible parts of Foundation in this file are
// Date.init(), Date.hashValue, and Date.description.
import Foundation.Date using (Date.init(), Date.hashValue, Date.description)
2) hiding: The hiding directive is followed by a list of identifiers within the imported module that should be hidden from this file.

// Imports all of Foundation.Date except `Date.compare()`
import Foundation.Date hiding (Date.compare())
3) renaming: The renaming directive is followed by a list of identifiers separated by to: that should be exposed to this file but renamed.

// Imports all of Dispatch.DispatchQueue but renames the static member
// DispatchQueue.main, to DispatchQueue.mainQueue
import Dispatch.DispatchQueue renaming (DispatchQueue.Type.main to: DispatchQueue.Type.mainQueue)
// Renaming can also rename modules. All members of UIKit have to be qualified with
// `UI` now.
import UIKit renaming (UIKit, to: UI)
Import directives chain to one another and can be used to create a fine-grained module import:

// Imports all of Foundation except `DateFormatter` and renames `Cache` to `LRUCache`
import Foundation hiding (DateFormatter) renaming (Cache to: LRUCache)
// Imports SCNNode except SCNNode.init(mdlObject:) and renames `.description` to
// `.nodeDescription`
import SceneKit using (SCNNode)
                renaming (SCNNode.description, to: SCNNode.nodeDescription)
                hiding (SCNNode.init(mdlObject:))
Directive chaining occurs left-to-right:

// This says to 1) Hide nothing 2) Use nothing 3) rename Int to INT. It is invalid
// because 1) We will show everything 2) Then hide everything 3) Therefore Int is unavailable, error.
import Swift hiding () using () renaming (Int, to: INT)
// 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. 3) Double is unavailable, error.
import Swift using (Int) hiding (String) renaming (Double, to: Triple)
// 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)
Module scope is delimited by the keyword module followed by a fully qualified name and must occur as the first declaration in a file. For example:

// ./Math/Integers/Arithmetic.swift
module Math.Integers.Arithmetic

public protocol _IntegerArithmetic {}

public struct _Abs {}

@_versioned
internal func _abs<Args>(_ args: Args) -> (_Abs, Args) {}

// ./Math/Integers.swift
module Math.Integers

// _abs is visible in this module and all others within the project,
// but is not exported along with it.
internal import Math.Integers.Arithmetic

public protocol IntegerArithmetic : _IntegerArithmetic, Comparable {}
public protocol SignedNumber : Comparable, ExpressibleByIntegerLiteral {}

// Math.swift
module Math

// Exports the entire public contents of Math.Integers, but nothing in
// Math.Integers.Arithmetic.
public import Math.Integers
Modules names are tied to a directory structure that describes their location relative to the current module and it will now be an error to violate this rule. For example:

module String // lives in ./String.swift
module String.Core // lives in ./String/Core.swift
module String.Core.Internals.Do.You.Even.Write // lives in ./String/Core/Internals/Do/You/Even/Write.swift
Existing projects that do not adopt these rules will still retain their implicit module name (usually defined as the name of the framework or application that is being built) and may continue to use whatever directory structure they wish, however they may not declare any explicit modules.

This proposal also solves the problem of module export. A module that is imported without an access level modifier will default to an internal import per usual. However, when it is useful to fully expose the public content of submodules to a client, a public modifier can be used. Similarly, when it is useful to access internal or [file]private APIs, but not expose them to clients, those access modifiers may be used. The rule of thumb is: Only identifiers that are at least as visible as the qualifier on the import make for valid import declarations. For example:

// A submodule declaring a `private` class that gets imported with
// an `internal` qualifier with a `using` directive is an invalid import
// declaration.
module Foo.Bar

private class PrivateThing {}

module Foo

// Error: PrivateThing not visible, use `private import`
import Foo.Bar using (PrivateThing)
// However, a submodule declaring a `public` struct that gets imported with
// an `private` qualifier is a valid import declaration.
module Foo.Bar

public class PublicThing {}

module Foo

// All good! Foo can see Foo.Bar.PrivateThing.
private import Foo.Bar using (PublicThing)
Because import directives are file-local, they will never be exported along with a public import and will default to exporting the entire contents of the module as though you had never declared them.

// In this file and this file alone, the directives apply. To the user
// of this module, it is as though this declaration were simply:
// public import Foundation.Date
public import Foundation.Date hiding (Date.init())
                              renaming (Date.Type.distantPast,
                                        to: Date.Type.letsGoLivingInThePast,
                                        Date.Type.timeIntervalSinceReferenceDate,
                                        to: Date.Type.startOfTheUniverse)
                              renaming (Date.Type.<, to: Date.Type.<<<<<)
<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. Code that is not organized into modules will remain unaffected and organized into one contiguous top-level module. However, it is strongly recommended that frameworks be decomposed and reorganized around the new module system.

As a case study, the public interface to the standard library appears to already be mostly broken down into submodules as described in GroupInfo.json <https://github.com/apple/swift/blob/master/stdlib/public/core/GroupInfo.json>.

Code that is defined in modulemaps already defines a module structure that can be imported directly into this scheme.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#alternatives-considered>Alternatives considered

Module export can also be placed on the module declaration itself. The relevant parts of the grammar that have changed are below with an example:

module-decl -> <access-level-modifier> module <module-path>
import-decl -> import <module-path> <(opt) import-directive-list>
private module String.Core.Internals

// Shh, it's a secret.
While this style makes it immediately obvious to the library author which modules are public or private, it causes the consumer problems because submodule exports are no longer explicit and are entirely ad-hoc. In the interest of enabling, for one, users of IDEs to drill into public submodules, making export local to import seems more appropriate.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Daniel Resnick) #11

Modules names are tied to a directory structure that describes their
location relative to the current module and it will now be an error to
violate this rule

What is the rationale for this rule? I realize this is a common convention
but what is gained from strictly enforcing it?

···

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

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

This is an interesting document. I think it deserves careful study. For
now, some questions:

What is the rationale behind permitting the using of specific methods?
This seems to be usually fine-grained in comparison to other languages.
What use cases do you have in mind for this?

One use case: Swift libraries export not just member references as I’ve
used here, but a large amount of free functions. It has long been a
problem that free functions seem to pollute a shared namespace and there
didn’t seem to be a clear way to hide them.

I can see the use case for hiding specific symbols when they come into
conflict with your own, but in your example you're hiding specific methods
declared *in* an imported type. What is the use case here? Is it going to
allow me to open backdoors so that, if I don't like `Foo.frobnicate()`, I
can hide it and then substitute my own in an extension? This seems like a
bad thing at first blush.

For members that would be an acceptable use-case. The worst-case scenario
that comes to mind is this being used as a way to “virtually override” a
method in a subclass. Then again, the scope of the damage is limited to
the file in which you’ve declared this monstrosity so clients and even you
will not be able to see it outside of there unless you explicitly redeclare
the hiding import (in which case, you probably know what you’re doing).

A use care here might be hiding the KVO-ish parts of an object from
yourself, or more generally subsetting out the part of an API you know you
shouldn’t interact with in a particular submodule.

I can see the obvious use case for renaming modules and types on
import--basically, in my mind, it's like typealiases with hiding, and it's
available in other languages of course. But how would renaming methods
work? If Foo conforms to Equatable and I rename `Foo.==` to `Foo.!=`, is
the type I import still Equatable? How would it behave? And even if Foo is
fine, what happens if I try to subclass my Frankensteinian Foo?

Of course you still conform to Equatable. The renaming defines a mapping
from your names to “proper" names. For example, if you use a renaming
import to change the requirements of a protocol in a file, then your
conformance will simply look at the mapping and see that everything
resolves into its proper place. Bear in mind that your renamings will not
survive outside of the file in which you declare them. Frankenteinian Foo
exists where you say it does and nowhere else. Everybody else just sees
Foo conform to Equatable (unless they rename things themselves).

On Mon, Jul 18, 2016 at 16:10 Robert Widmann via swift-evolution < > swift-evolution@swift.org> wrote:

Hello all,

TJ Usiyan, Harlan Haskins, and I have been working on a proposal to
rework qualified imports and introduce an explicit module system to Swift
that we’d like to publish for your viewing pleasure.

The initial impetus was set out in a radar (rdar://17630570) I sent
fairly early on that didn’t receive a response, so I started a
swift-evolution
<http://permalink.gmane.org/gmane.comp.lang.swift.evolution/1378> thread
discussing the basics of this proposal. It has been refined and expanded a
bit to include an effort to make Swift modules explicit and updated with
the feedback of that first thread. Contents of the proposal are inline and can
also be had as a gist
<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6> or on
Github. <https://github.com/apple/swift-evolution/pull/440>

Cheers,

~Robert Widmann

Qualified Imports and Modules

   - Proposal: SE-NNNN
   <https://gist.github.com/CodaFi/NNNN-first-class-qualified-imports.md>
   - Authors: Robert Widmann <https://github.com/codafi>, Harlan Haskins
   <https://github.com/harlanhaskins>, 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 and the introduction of a module system.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#motivation>
Motivation

Swift code is modular by default. However, it is not clear how to
decompose existing modules further into submodules. In addition, it is
difficult to tell how importing a module affects its export to consumers of
a library. This leads many to either fake namespaces with enums, attempt to
structure Swift code with modulemaps, or use a large amount of
version-control submodules. All of these can be rolled into one complete
package in the form of a comprehensive rethink of the qualified import
system and the introduction of a module system.

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

Modules will now become an explicit part of working with canonical Swift
code. The grammar and semantics of qualified imports will change completely
with the addition of *import qualifiers* and *import directives*. We
also introduce three new contextual keywords: using, hiding, and renaming,
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

module-decl -> module <module-path>
import-decl -> <access-level-modifier> import <module-path> <(opt) import-directive-list>
module-path -> <identifier>
            -> <identifier>.<import-path>
import-directive-list -> <import-directive>
                      -> <import-directive> <import-directive-list>
import-directive -> using (<identifier>, ...)
                 -> hiding (<identifier>, ...)
                 -> renaming (<identifier>, to: <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 3 operations:

1) *using*: The *using* directive is followed by a list of identifiers
within the imported module that should be exposed to this file.

// The only visible parts of Foundation in this file are // Date.init(), Date.hashValue, and Date.description.import Foundation.Date using (Date.init(), Date.hashValue, Date.description)

2) *hiding*: The hiding directive is followed by a list of identifiers
within the imported module that should be hidden from this file.

// Imports all of Foundation.Date except `Date.compare()`import Foundation.Date hiding (Date.compare())

3) *renaming*: The renaming directive is followed by a list of
identifiers separated by to: that should be exposed to this file but
renamed.

// Imports all of Dispatch.DispatchQueue but renames the static member // DispatchQueue.main, to DispatchQueue.mainQueueimport Dispatch.DispatchQueue renaming (DispatchQueue.Type.main to: DispatchQueue.Type.mainQueue)// Renaming can also rename modules. All members of UIKit have to be qualified with// `UI` now.import UIKit renaming (UIKit, to: UI)

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

// Imports all of Foundation except `DateFormatter` and renames `Cache` to `LRUCache`import Foundation hiding (DateFormatter) renaming (Cache to: LRUCache)// Imports SCNNode except SCNNode.init(mdlObject:) and renames `.description` to// `.nodeDescription` import SceneKit using (SCNNode)
                renaming (SCNNode.description, to: SCNNode.nodeDescription)
                hiding (SCNNode.init(mdlObject:))

Directive chaining occurs left-to-right:

// This says to 1) Hide nothing 2) Use nothing 3) rename Int to INT. It is invalid// because 1) We will show everything 2) Then hide everything 3) Therefore Int is unavailable, error.import Swift hiding () using () renaming (Int, to: INT)// 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. 3) Double is unavailable, error.import Swift using (Int) hiding (String) renaming (Double, to: Triple)// 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)

Module scope is delimited by the keyword module followed by a fully
qualified name and must occur as the first declaration in a file. For
example:

// ./Math/Integers/Arithmetic.swift
module Math.Integers.Arithmetic
public protocol _IntegerArithmetic {}
public struct _Abs {}
@_versionedinternal func _abs<Args>(_ args: Args) -> (_Abs, Args) {}
// ./Math/Integers.swift
module Math.Integers
// _abs is visible in this module and all others within the project, // but is not exported along with it.internal import Math.Integers.Arithmetic
public protocol IntegerArithmetic : _IntegerArithmetic, Comparable {}public protocol SignedNumber : Comparable, ExpressibleByIntegerLiteral {}

// Math.swift
module Math
// Exports the entire public contents of Math.Integers, but nothing in // Math.Integers.Arithmetic.public import Math.Integers

Modules names are tied to a directory structure that describes their
location relative to the current module and it will now be an error to
violate this rule. For example:

module String // lives in ./String.swift
module String.Core // lives in ./String/Core.swift
module String.Core.Internals.Do.You.Even.Write // lives in ./String/Core/Internals/Do/You/Even/Write.swift

Existing projects that do not adopt these rules will still retain their *implicit
module name* (usually defined as the name of the framework or
application that is being built) and may continue to use whatever directory
structure they wish, however they may not declare any explicit modules.

This proposal also solves the problem of module *export*. A module that
is imported without an access level modifier will default to an internal import
per usual. However, when it is useful to fully expose the public content of
submodules to a client, a public modifier can be used. Similarly, when
it is useful to access internal or [file]private APIs, but not expose
them to clients, those access modifiers may be used. The rule of thumb is:
Only identifiers that are at least as visible as the qualifier on the
import make for valid import declarations. For example:

// A submodule declaring a `private` class that gets imported with // an `internal` qualifier with a `using` directive is an invalid import // declaration.
module Foo.Bar
private class PrivateThing {}

module Foo
// Error: PrivateThing not visible, use `private import`import Foo.Bar using (PrivateThing)

// However, a submodule declaring a `public` struct that gets imported with // an `private` qualifier is a valid import declaration.
module Foo.Bar
public class PublicThing {}

module Foo
// All good! Foo can see Foo.Bar.PrivateThing.private import Foo.Bar using (PublicThing)

Because import directives are file-local, they will never be exported
along with a public import and will default to exporting the entire
contents of the module as though you had never declared them.

// In this file and this file alone, the directives apply. To the user// of this module, it is as though this declaration were simply:// public import Foundation.Datepublic import Foundation.Date hiding (Date.init())
                              renaming (Date.Type.distantPast,
                                        to: Date.Type.letsGoLivingInThePast,
                                        Date.Type.timeIntervalSinceReferenceDate,
                                        to: Date.Type.startOfTheUniverse)
                              renaming (Date.Type.<, to: Date.Type.<<<<<)

<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. Code that is not organized into modules will remain
unaffected and organized into one contiguous top-level module. However, it
is strongly recommended that frameworks be decomposed and reorganized
around the new module system.

As a case study, the public interface to the standard library appears to
already be mostly broken down into submodules as described in
GroupInfo.json
<https://github.com/apple/swift/blob/master/stdlib/public/core/GroupInfo.json>
.

Code that is defined in modulemaps already defines a module structure
that can be imported directly into this scheme.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#alternatives-considered>Alternatives
considered

Module export can also be placed on the module declaration itself. The
relevant parts of the grammar that have changed are below with an example:

module-decl -> <access-level-modifier> module <module-path>
import-decl -> import <module-path> <(opt) import-directive-list>

private module String.Core.Internals
// Shh, it's a secret.

While this style makes it immediately obvious to the library author which
modules are public or private, it causes the consumer problems because
submodule exports are no longer explicit and are entirely ad-hoc. In the
interest of enabling, for one, users of IDEs to drill into public
submodules, making export local to import seems more appropriate.
_______________________________________________
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) #12

This is an interesting document. I think it deserves careful study. For now, some questions:

What is the rationale behind permitting the using of specific methods? This seems to be usually fine-grained in comparison to other languages. What use cases do you have in mind for this?

One use case: Swift libraries export not just member references as I’ve used here, but a large amount of free functions. It has long been a problem that free functions seem to pollute a shared namespace and there didn’t seem to be a clear way to hide them.

Would a plausible simplification of the proposal be to have it fine-grained enough to address free functions but not methods inside types?
Incidentally, although I do not see it in the proposal, I assume that * in some form will be permitted (as in, `import Foundation using *`).

I can see the use case for hiding specific symbols when they come into conflict with your own, but in your example you're hiding specific methods declared *in* an imported type. What is the use case here? Is it going to allow me to open backdoors so that, if I don't like `Foo.frobnicate()`, I can hide it and then substitute my own in an extension? This seems like a bad thing at first blush.

For members that would be an acceptable use-case. The worst-case scenario that comes to mind is this being used as a way to “virtually override” a method in a subclass. Then again, the scope of the damage is limited to the file in which you’ve declared this monstrosity so clients and even you will not be able to see it outside of there unless you explicitly redeclare the hiding import (in which case, you probably know what you’re doing).

A use care here might be hiding the KVO-ish parts of an object from yourself, or more generally subsetting out the part of an API you know you shouldn’t interact with in a particular submodule.

I can see the obvious use case for renaming modules and types on import--basically, in my mind, it's like typealiases with hiding, and it's available in other languages of course. But how would renaming methods work? If Foo conforms to Equatable and I rename `Foo.==` to `Foo.!=`, is the type I import still Equatable? How would it behave? And even if Foo is fine, what happens if I try to subclass my Frankensteinian Foo?

Of course you still conform to Equatable. The renaming defines a mapping from your names to “proper" names. For example, if you use a renaming import to change the requirements of a protocol in a file, then your conformance will simply look at the mapping and see that everything resolves into its proper place. Bear in mind that your renamings will not survive outside of the file in which you declare them. Frankenteinian Foo exists where you say it does and nowhere else. Everybody else just sees Foo conform to Equatable (unless they rename things themselves).

Maybe let's work through an example:

Suppose we have in stdlib:

public protocol FooProtocol {
  func frobnicate()
}

Now, I write a library:

import Swift.FooProtocol renaming (FooProtocol.frobnicate(), to: FooProtocol.bobnicate())

public open class MyFoo : Swift.FooProtocol {
  public open func bobnicate() {
    print("Does your head hurt yet?")
  }
}

Now, you are an end user of my sinister library.

What is the public API of `MyFoo`?

The proposal addresses this

Because import directives are file-local, they will never be exported along with a `public` import and will
default to exporting the entire contents of the module as though you had never declared them.

The user (and even you in other files that import this module) will see a protocol conformance exactly as laid out in the Swift.FooProtocol module.

For you, does `MyFoo` conform to `Swift.FooProtocol`?

It conforms because the renaming you wrote describes a way of resolving FooProtocol.bobnicate() (your API) to FooProtocol.frobnicate() (everybody else’s API).

Can you call `MyFoo.frobnicate()`? How about `MyFoo.bobnicate()`?

What if you try to subclass `MyFoo`?

If you are inside the module you wrote the renaming, you will use it. If you are outside of it, you will see the protocol requirement sans renaming.

Does your subclass still conform to `Swift.FooProtocol`?
Do you override `bobnicate()` or `frobnicate()`?
My head hurts…

Because you have explicitly renamed the protocol requirement, you will override the same protocol requirement both inside and outside this module but your renaming will not propagate to other files unless they themselves opt in the way you have here. It would be particularly sinister if you could arbitrarily edit the user-facing API of members simply by importing a library.

···

On Jul 18, 2016, at 3:00 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Mon, Jul 18, 2016 at 4:49 PM, Robert Widmann <rwidmann@apple.com <mailto:rwidmann@apple.com>> wrote:

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

On Mon, Jul 18, 2016 at 16:10 Robert Widmann via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Hello all,

TJ Usiyan, Harlan Haskins, and I have been working on a proposal to rework qualified imports and introduce an explicit module system to Swift that we’d like to publish for your viewing pleasure.

The initial impetus was set out in a radar (rdar://17630570 <>) I sent fairly early on that didn’t receive a response, so I started a swift-evolution <http://permalink.gmane.org/gmane.comp.lang.swift.evolution/1378> thread discussing the basics of this proposal. It has been refined and expanded a bit to include an effort to make Swift modules explicit and updated with the feedback of that first thread. Contents of the proposal are inline and can also be had as a gist <https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6> or on Github. <https://github.com/apple/swift-evolution/pull/440>

Cheers,

~Robert Widmann

Qualified Imports and Modules

Proposal: SE-NNNN <https://gist.github.com/CodaFi/NNNN-first-class-qualified-imports.md>
Authors: Robert Widmann <https://github.com/codafi>, Harlan Haskins <https://github.com/harlanhaskins>, 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 and the introduction of a module system.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#motivation>Motivation

Swift code is modular by default. However, it is not clear how to decompose existing modules further into submodules. In addition, it is difficult to tell how importing a module affects its export to consumers of a library. This leads many to either fake namespaces with enums, attempt to structure Swift code with modulemaps, or use a large amount of version-control submodules. All of these can be rolled into one complete package in the form of a comprehensive rethink of the qualified import system and the introduction of a module system.

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

Modules will now become an explicit part of working with canonical Swift code. The grammar and semantics of qualified imports will change completely with the addition of import qualifiers and import directives. We also introduce three new contextual keywords: using, hiding, and renaming, 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

module-decl -> module <module-path>
import-decl -> <access-level-modifier> import <module-path> <(opt) import-directive-list>
module-path -> <identifier>
            -> <identifier>.<import-path>
import-directive-list -> <import-directive>
                      -> <import-directive> <import-directive-list>
import-directive -> using (<identifier>, ...)
                 -> hiding (<identifier>, ...)
                 -> renaming (<identifier>, to: <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 3 operations:

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

// The only visible parts of Foundation in this file are
// Date.init(), Date.hashValue, and Date.description.
import Foundation.Date using (Date.init(), Date.hashValue, Date.description)
2) hiding: The hiding directive is followed by a list of identifiers within the imported module that should be hidden from this file.

// Imports all of Foundation.Date except `Date.compare()`
import Foundation.Date hiding (Date.compare())
3) renaming: The renaming directive is followed by a list of identifiers separated by to: that should be exposed to this file but renamed.

// Imports all of Dispatch.DispatchQueue but renames the static member
// DispatchQueue.main, to DispatchQueue.mainQueue
import Dispatch.DispatchQueue renaming (DispatchQueue.Type.main to: DispatchQueue.Type.mainQueue)
// Renaming can also rename modules. All members of UIKit have to be qualified with
// `UI` now.
import UIKit renaming (UIKit, to: UI)
Import directives chain to one another and can be used to create a fine-grained module import:

// Imports all of Foundation except `DateFormatter` and renames `Cache` to `LRUCache`
import Foundation hiding (DateFormatter) renaming (Cache to: LRUCache)
// Imports SCNNode except SCNNode.init(mdlObject:) and renames `.description` to
// `.nodeDescription`
import SceneKit using (SCNNode)
                renaming (SCNNode.description, to: SCNNode.nodeDescription)
                hiding (SCNNode.init(mdlObject:))
Directive chaining occurs left-to-right:

// This says to 1) Hide nothing 2) Use nothing 3) rename Int to INT. It is invalid
// because 1) We will show everything 2) Then hide everything 3) Therefore Int is unavailable, error.
import Swift hiding () using () renaming (Int, to: INT)
// 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. 3) Double is unavailable, error.
import Swift using (Int) hiding (String) renaming (Double, to: Triple)
// 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)
Module scope is delimited by the keyword module followed by a fully qualified name and must occur as the first declaration in a file. For example:

// ./Math/Integers/Arithmetic.swift
module Math.Integers.Arithmetic

public protocol _IntegerArithmetic {}

public struct _Abs {}

@_versioned
internal func _abs<Args>(_ args: Args) -> (_Abs, Args) {}

// ./Math/Integers.swift
module Math.Integers

// _abs is visible in this module and all others within the project,
// but is not exported along with it.
internal import Math.Integers.Arithmetic

public protocol IntegerArithmetic : _IntegerArithmetic, Comparable {}
public protocol SignedNumber : Comparable, ExpressibleByIntegerLiteral {}

// Math.swift
module Math

// Exports the entire public contents of Math.Integers, but nothing in
// Math.Integers.Arithmetic.
public import Math.Integers
Modules names are tied to a directory structure that describes their location relative to the current module and it will now be an error to violate this rule. For example:

module String // lives in ./String.swift
module String.Core // lives in ./String/Core.swift
module String.Core.Internals.Do.You.Even.Write // lives in ./String/Core/Internals/Do/You/Even/Write.swift
Existing projects that do not adopt these rules will still retain their implicit module name (usually defined as the name of the framework or application that is being built) and may continue to use whatever directory structure they wish, however they may not declare any explicit modules.

This proposal also solves the problem of module export. A module that is imported without an access level modifier will default to an internal import per usual. However, when it is useful to fully expose the public content of submodules to a client, a public modifier can be used. Similarly, when it is useful to access internal or [file]private APIs, but not expose them to clients, those access modifiers may be used. The rule of thumb is: Only identifiers that are at least as visible as the qualifier on the import make for valid import declarations. For example:

// A submodule declaring a `private` class that gets imported with
// an `internal` qualifier with a `using` directive is an invalid import
// declaration.
module Foo.Bar

private class PrivateThing {}

module Foo

// Error: PrivateThing not visible, use `private import`
import Foo.Bar using (PrivateThing)
// However, a submodule declaring a `public` struct that gets imported with
// an `private` qualifier is a valid import declaration.
module Foo.Bar

public class PublicThing {}

module Foo

// All good! Foo can see Foo.Bar.PrivateThing.
private import Foo.Bar using (PublicThing)
Because import directives are file-local, they will never be exported along with a public import and will default to exporting the entire contents of the module as though you had never declared them.

// In this file and this file alone, the directives apply. To the user
// of this module, it is as though this declaration were simply:
// public import Foundation.Date
public import Foundation.Date hiding (Date.init())
                              renaming (Date.Type.distantPast,
                                        to: Date.Type.letsGoLivingInThePast,
                                        Date.Type.timeIntervalSinceReferenceDate,
                                        to: Date.Type.startOfTheUniverse)
                              renaming (Date.Type.<, to: Date.Type.<<<<<)
<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. Code that is not organized into modules will remain unaffected and organized into one contiguous top-level module. However, it is strongly recommended that frameworks be decomposed and reorganized around the new module system.

As a case study, the public interface to the standard library appears to already be mostly broken down into submodules as described in GroupInfo.json <https://github.com/apple/swift/blob/master/stdlib/public/core/GroupInfo.json>.

Code that is defined in modulemaps already defines a module structure that can be imported directly into this scheme.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#alternatives-considered>Alternatives considered

Module export can also be placed on the module declaration itself. The relevant parts of the grammar that have changed are below with an example:

module-decl -> <access-level-modifier> module <module-path>
import-decl -> import <module-path> <(opt) import-directive-list>
private module String.Core.Internals

// Shh, it's a secret.
While this style makes it immediately obvious to the library author which modules are public or private, it causes the consumer problems because submodule exports are no longer explicit and are entirely ad-hoc. In the interest of enabling, for one, users of IDEs to drill into public submodules, making export local to import seems more appropriate.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Beta) #13

Modules names are tied to a directory structure that describes their location relative to the current module and it will now be an error to violate this rule
What is the rationale for this rule? I realize this is a common convention but what is gained from strictly enforcing it?

Outside of general organizational cleanliness? A big reason to favor this over a free-for-all is that a module becomes tied to something physical. These are not C++-style namespaces that admit the kind of behavior you might be thinking of sans restriction. And with it on, there is nothing stopping you from declaring whatever you wish in any particular module space and re-exporting it the proper way. For example

module String // publicly imports
module String.Core // which publicly imports
module String.Core.Algorithms // which internally imports
module String.Core.Algorithms.ExperimentsAndOtherNonsense

You can have your cake and eat it too, you just can’t put it wherever you’d like without being consistent both within and without. We also allow you to opt out of this behavior entirely if you have a particular organizational structure you would like to use other than modules.

···

On Jul 18, 2016, at 2:57 PM, Daniel Resnick <danielzresnick@gmail.com> wrote:

Existing projects that do not adopt these rules will still retain their implicit module name
(usually defined as the name of the framework or application that is being built) and may continue to use
whatever directory structure they wish, however they may not declare any explicit modules.

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

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

This is an interesting document. I think it deserves careful study. For now, some questions:

What is the rationale behind permitting the using of specific methods? This seems to be usually fine-grained in comparison to other languages. What use cases do you have in mind for this?

One use case: Swift libraries export not just member references as I’ve used here, but a large amount of free functions. It has long been a problem that free functions seem to pollute a shared namespace and there didn’t seem to be a clear way to hide them.

I can see the use case for hiding specific symbols when they come into conflict with your own, but in your example you're hiding specific methods declared *in* an imported type. What is the use case here? Is it going to allow me to open backdoors so that, if I don't like `Foo.frobnicate()`, I can hide it and then substitute my own in an extension? This seems like a bad thing at first blush.

For members that would be an acceptable use-case. The worst-case scenario that comes to mind is this being used as a way to “virtually override” a method in a subclass. Then again, the scope of the damage is limited to the file in which you’ve declared this monstrosity so clients and even you will not be able to see it outside of there unless you explicitly redeclare the hiding import (in which case, you probably know what you’re doing).

A use care here might be hiding the KVO-ish parts of an object from yourself, or more generally subsetting out the part of an API you know you shouldn’t interact with in a particular submodule.

I can see the obvious use case for renaming modules and types on import--basically, in my mind, it's like typealiases with hiding, and it's available in other languages of course. But how would renaming methods work? If Foo conforms to Equatable and I rename `Foo.==` to `Foo.!=`, is the type I import still Equatable? How would it behave? And even if Foo is fine, what happens if I try to subclass my Frankensteinian Foo?

Of course you still conform to Equatable. The renaming defines a mapping from your names to “proper" names. For example, if you use a renaming import to change the requirements of a protocol in a file, then your conformance will simply look at the mapping and see that everything resolves into its proper place. Bear in mind that your renamings will not survive outside of the file in which you declare them. Frankenteinian Foo exists where you say it does and nowhere else. Everybody else just sees Foo conform to Equatable (unless they rename things themselves).

On Mon, Jul 18, 2016 at 16:10 Robert Widmann via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Hello all,

TJ Usiyan, Harlan Haskins, and I have been working on a proposal to rework qualified imports and introduce an explicit module system to Swift that we’d like to publish for your viewing pleasure.

The initial impetus was set out in a radar (rdar://17630570 <>) I sent fairly early on that didn’t receive a response, so I started a swift-evolution <http://permalink.gmane.org/gmane.comp.lang.swift.evolution/1378> thread discussing the basics of this proposal. It has been refined and expanded a bit to include an effort to make Swift modules explicit and updated with the feedback of that first thread. Contents of the proposal are inline and can also be had as a gist <https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6> or on Github. <https://github.com/apple/swift-evolution/pull/440>

Cheers,

~Robert Widmann

Qualified Imports and Modules

Proposal: SE-NNNN <https://gist.github.com/CodaFi/NNNN-first-class-qualified-imports.md>
Authors: Robert Widmann <https://github.com/codafi>, Harlan Haskins <https://github.com/harlanhaskins>, 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 and the introduction of a module system.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#motivation>Motivation

Swift code is modular by default. However, it is not clear how to decompose existing modules further into submodules. In addition, it is difficult to tell how importing a module affects its export to consumers of a library. This leads many to either fake namespaces with enums, attempt to structure Swift code with modulemaps, or use a large amount of version-control submodules. All of these can be rolled into one complete package in the form of a comprehensive rethink of the qualified import system and the introduction of a module system.

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

Modules will now become an explicit part of working with canonical Swift code. The grammar and semantics of qualified imports will change completely with the addition of import qualifiers and import directives. We also introduce three new contextual keywords: using, hiding, and renaming, 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

module-decl -> module <module-path>
import-decl -> <access-level-modifier> import <module-path> <(opt) import-directive-list>
module-path -> <identifier>
            -> <identifier>.<import-path>
import-directive-list -> <import-directive>
                      -> <import-directive> <import-directive-list>
import-directive -> using (<identifier>, ...)
                 -> hiding (<identifier>, ...)
                 -> renaming (<identifier>, to: <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 3 operations:

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

// The only visible parts of Foundation in this file are
// Date.init(), Date.hashValue, and Date.description.
import Foundation.Date using (Date.init(), Date.hashValue, Date.description)
2) hiding: The hiding directive is followed by a list of identifiers within the imported module that should be hidden from this file.

// Imports all of Foundation.Date except `Date.compare()`
import Foundation.Date hiding (Date.compare())
3) renaming: The renaming directive is followed by a list of identifiers separated by to: that should be exposed to this file but renamed.

// Imports all of Dispatch.DispatchQueue but renames the static member
// DispatchQueue.main, to DispatchQueue.mainQueue
import Dispatch.DispatchQueue renaming (DispatchQueue.Type.main to: DispatchQueue.Type.mainQueue)
// Renaming can also rename modules. All members of UIKit have to be qualified with
// `UI` now.
import UIKit renaming (UIKit, to: UI)
Import directives chain to one another and can be used to create a fine-grained module import:

// Imports all of Foundation except `DateFormatter` and renames `Cache` to `LRUCache`
import Foundation hiding (DateFormatter) renaming (Cache to: LRUCache)
// Imports SCNNode except SCNNode.init(mdlObject:) and renames `.description` to
// `.nodeDescription`
import SceneKit using (SCNNode)
                renaming (SCNNode.description, to: SCNNode.nodeDescription)
                hiding (SCNNode.init(mdlObject:))
Directive chaining occurs left-to-right:

// This says to 1) Hide nothing 2) Use nothing 3) rename Int to INT. It is invalid
// because 1) We will show everything 2) Then hide everything 3) Therefore Int is unavailable, error.
import Swift hiding () using () renaming (Int, to: INT)
// 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. 3) Double is unavailable, error.
import Swift using (Int) hiding (String) renaming (Double, to: Triple)
// 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)
Module scope is delimited by the keyword module followed by a fully qualified name and must occur as the first declaration in a file. For example:

// ./Math/Integers/Arithmetic.swift
module Math.Integers.Arithmetic

public protocol _IntegerArithmetic {}

public struct _Abs {}

@_versioned
internal func _abs<Args>(_ args: Args) -> (_Abs, Args) {}

// ./Math/Integers.swift
module Math.Integers

// _abs is visible in this module and all others within the project,
// but is not exported along with it.
internal import Math.Integers.Arithmetic

public protocol IntegerArithmetic : _IntegerArithmetic, Comparable {}
public protocol SignedNumber : Comparable, ExpressibleByIntegerLiteral {}

// Math.swift
module Math

// Exports the entire public contents of Math.Integers, but nothing in
// Math.Integers.Arithmetic.
public import Math.Integers
Modules names are tied to a directory structure that describes their location relative to the current module and it will now be an error to violate this rule. For example:

module String // lives in ./String.swift
module String.Core // lives in ./String/Core.swift
module String.Core.Internals.Do.You.Even.Write // lives in ./String/Core/Internals/Do/You/Even/Write.swift
Existing projects that do not adopt these rules will still retain their implicit module name (usually defined as the name of the framework or application that is being built) and may continue to use whatever directory structure they wish, however they may not declare any explicit modules.

This proposal also solves the problem of module export. A module that is imported without an access level modifier will default to an internal import per usual. However, when it is useful to fully expose the public content of submodules to a client, a public modifier can be used. Similarly, when it is useful to access internal or [file]private APIs, but not expose them to clients, those access modifiers may be used. The rule of thumb is: Only identifiers that are at least as visible as the qualifier on the import make for valid import declarations. For example:

// A submodule declaring a `private` class that gets imported with
// an `internal` qualifier with a `using` directive is an invalid import
// declaration.
module Foo.Bar

private class PrivateThing {}

module Foo

// Error: PrivateThing not visible, use `private import`
import Foo.Bar using (PrivateThing)
// However, a submodule declaring a `public` struct that gets imported with
// an `private` qualifier is a valid import declaration.
module Foo.Bar

public class PublicThing {}

module Foo

// All good! Foo can see Foo.Bar.PrivateThing.
private import Foo.Bar using (PublicThing)
Because import directives are file-local, they will never be exported along with a public import and will default to exporting the entire contents of the module as though you had never declared them.

// In this file and this file alone, the directives apply. To the user
// of this module, it is as though this declaration were simply:
// public import Foundation.Date
public import Foundation.Date hiding (Date.init())
                              renaming (Date.Type.distantPast,
                                        to: Date.Type.letsGoLivingInThePast,
                                        Date.Type.timeIntervalSinceReferenceDate,
                                        to: Date.Type.startOfTheUniverse)
                              renaming (Date.Type.<, to: Date.Type.<<<<<)
<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. Code that is not organized into modules will remain unaffected and organized into one contiguous top-level module. However, it is strongly recommended that frameworks be decomposed and reorganized around the new module system.

As a case study, the public interface to the standard library appears to already be mostly broken down into submodules as described in GroupInfo.json <https://github.com/apple/swift/blob/master/stdlib/public/core/GroupInfo.json>.

Code that is defined in modulemaps already defines a module structure that can be imported directly into this scheme.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#alternatives-considered>Alternatives considered

Module export can also be placed on the module declaration itself. The relevant parts of the grammar that have changed are below with an example:

module-decl -> <access-level-modifier> module <module-path>
import-decl -> import <module-path> <(opt) import-directive-list>
private module String.Core.Internals

// Shh, it's a secret.
While this style makes it immediately obvious to the library author which modules are public or private, it causes the consumer problems because submodule exports are no longer explicit and are entirely ad-hoc. In the interest of enabling, for one, users of IDEs to drill into public submodules, making export local to import seems more appropriate.
_______________________________________________
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


(Beta) #14

is changed so that, outside the module, it has a member `frobnicate()` and no longer has a member `bobnicate()`?

*Unchanged. Other than that, you’ve got it! I wanted to make *very* sure that whatever APIs come in are the same ones that go out regardless of local changes.

···

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

On Mon, Jul 18, 2016 at 5:09 PM, Robert Widmann <rwidmann@apple.com <mailto:rwidmann@apple.com>> wrote:

On Jul 18, 2016, at 3:00 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:

On Mon, Jul 18, 2016 at 4:49 PM, Robert Widmann <rwidmann@apple.com <mailto:rwidmann@apple.com>> wrote:

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

This is an interesting document. I think it deserves careful study. For now, some questions:

What is the rationale behind permitting the using of specific methods? This seems to be usually fine-grained in comparison to other languages. What use cases do you have in mind for this?

One use case: Swift libraries export not just member references as I’ve used here, but a large amount of free functions. It has long been a problem that free functions seem to pollute a shared namespace and there didn’t seem to be a clear way to hide them.

Would a plausible simplification of the proposal be to have it fine-grained enough to address free functions but not methods inside types?
Incidentally, although I do not see it in the proposal, I assume that * in some form will be permitted (as in, `import Foundation using *`).

I can see the use case for hiding specific symbols when they come into conflict with your own, but in your example you're hiding specific methods declared *in* an imported type. What is the use case here? Is it going to allow me to open backdoors so that, if I don't like `Foo.frobnicate()`, I can hide it and then substitute my own in an extension? This seems like a bad thing at first blush.

For members that would be an acceptable use-case. The worst-case scenario that comes to mind is this being used as a way to “virtually override” a method in a subclass. Then again, the scope of the damage is limited to the file in which you’ve declared this monstrosity so clients and even you will not be able to see it outside of there unless you explicitly redeclare the hiding import (in which case, you probably know what you’re doing).

A use care here might be hiding the KVO-ish parts of an object from yourself, or more generally subsetting out the part of an API you know you shouldn’t interact with in a particular submodule.

I can see the obvious use case for renaming modules and types on import--basically, in my mind, it's like typealiases with hiding, and it's available in other languages of course. But how would renaming methods work? If Foo conforms to Equatable and I rename `Foo.==` to `Foo.!=`, is the type I import still Equatable? How would it behave? And even if Foo is fine, what happens if I try to subclass my Frankensteinian Foo?

Of course you still conform to Equatable. The renaming defines a mapping from your names to “proper" names. For example, if you use a renaming import to change the requirements of a protocol in a file, then your conformance will simply look at the mapping and see that everything resolves into its proper place. Bear in mind that your renamings will not survive outside of the file in which you declare them. Frankenteinian Foo exists where you say it does and nowhere else. Everybody else just sees Foo conform to Equatable (unless they rename things themselves).

Maybe let's work through an example:

Suppose we have in stdlib:

public protocol FooProtocol {
  func frobnicate()
}

Now, I write a library:

import Swift.FooProtocol renaming (FooProtocol.frobnicate(), to: FooProtocol.bobnicate())

public open class MyFoo : Swift.FooProtocol {
  public open func bobnicate() {
    print("Does your head hurt yet?")
  }
}

Now, you are an end user of my sinister library.

What is the public API of `MyFoo`?

The proposal addresses this

> Because import directives are file-local, they will never be exported along with a `public` import and will
> default to exporting the entire contents of the module as though you had never declared them.

The user (and even you in other files that import this module) will see a protocol conformance exactly as laid out in the Swift.FooProtocol module.

For you, does `MyFoo` conform to `Swift.FooProtocol`?

It conforms because the renaming you wrote describes a way of resolving FooProtocol.bobnicate() (your API) to FooProtocol.frobnicate() (everybody else’s API).

Can you call `MyFoo.frobnicate()`? How about `MyFoo.bobnicate()`?

What if you try to subclass `MyFoo`?

If you are inside the module you wrote the renaming, you will use it. If you are outside of it, you will see the protocol requirement sans renaming.

Does your subclass still conform to `Swift.FooProtocol`?
Do you override `bobnicate()` or `frobnicate()`?
My head hurts…

Because you have explicitly renamed the protocol requirement, you will override the same protocol requirement both inside and outside this module but your renaming will not propagate to other files unless they themselves opt in the way you have here. It would be particularly sinister if you could arbitrarily edit the user-facing API of members simply by importing a library.

Sounds good. If I understand you correctly, by conforming `MyFoo` to an internally renamed `Swift.FooProtocol`, the renaming of the user-facing API for `FooProtocol` means that the public API of `MyFoo` is changed so that, outside the module, it has a member `frobnicate()` and no longer has a member `bobnicate()`?

On Mon, Jul 18, 2016 at 16:10 Robert Widmann via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Hello all,

TJ Usiyan, Harlan Haskins, and I have been working on a proposal to rework qualified imports and introduce an explicit module system to Swift that we’d like to publish for your viewing pleasure.

The initial impetus was set out in a radar (rdar://17630570 <>) I sent fairly early on that didn’t receive a response, so I started a swift-evolution <http://permalink.gmane.org/gmane.comp.lang.swift.evolution/1378> thread discussing the basics of this proposal. It has been refined and expanded a bit to include an effort to make Swift modules explicit and updated with the feedback of that first thread. Contents of the proposal are inline and can also be had as a gist <https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6> or on Github. <https://github.com/apple/swift-evolution/pull/440>

Cheers,

~Robert Widmann

Qualified Imports and Modules

Proposal: SE-NNNN <https://gist.github.com/CodaFi/NNNN-first-class-qualified-imports.md>
Authors: Robert Widmann <https://github.com/codafi>, Harlan Haskins <https://github.com/harlanhaskins>, 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 and the introduction of a module system.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#motivation>Motivation

Swift code is modular by default. However, it is not clear how to decompose existing modules further into submodules. In addition, it is difficult to tell how importing a module affects its export to consumers of a library. This leads many to either fake namespaces with enums, attempt to structure Swift code with modulemaps, or use a large amount of version-control submodules. All of these can be rolled into one complete package in the form of a comprehensive rethink of the qualified import system and the introduction of a module system.

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

Modules will now become an explicit part of working with canonical Swift code. The grammar and semantics of qualified imports will change completely with the addition of import qualifiers and import directives. We also introduce three new contextual keywords: using, hiding, and renaming, 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

module-decl -> module <module-path>
import-decl -> <access-level-modifier> import <module-path> <(opt) import-directive-list>
module-path -> <identifier>
            -> <identifier>.<import-path>
import-directive-list -> <import-directive>
                      -> <import-directive> <import-directive-list>
import-directive -> using (<identifier>, ...)
                 -> hiding (<identifier>, ...)
                 -> renaming (<identifier>, to: <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 3 operations:

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

// The only visible parts of Foundation in this file are
// Date.init(), Date.hashValue, and Date.description.
import Foundation.Date using (Date.init(), Date.hashValue, Date.description)
2) hiding: The hiding directive is followed by a list of identifiers within the imported module that should be hidden from this file.

// Imports all of Foundation.Date except `Date.compare()`
import Foundation.Date hiding (Date.compare())
3) renaming: The renaming directive is followed by a list of identifiers separated by to: that should be exposed to this file but renamed.

// Imports all of Dispatch.DispatchQueue but renames the static member
// DispatchQueue.main, to DispatchQueue.mainQueue
import Dispatch.DispatchQueue renaming (DispatchQueue.Type.main to: DispatchQueue.Type.mainQueue)
// Renaming can also rename modules. All members of UIKit have to be qualified with
// `UI` now.
import UIKit renaming (UIKit, to: UI)
Import directives chain to one another and can be used to create a fine-grained module import:

// Imports all of Foundation except `DateFormatter` and renames `Cache` to `LRUCache`
import Foundation hiding (DateFormatter) renaming (Cache to: LRUCache)
// Imports SCNNode except SCNNode.init(mdlObject:) and renames `.description` to
// `.nodeDescription`
import SceneKit using (SCNNode)
                renaming (SCNNode.description, to: SCNNode.nodeDescription)
                hiding (SCNNode.init(mdlObject:))
Directive chaining occurs left-to-right:

// This says to 1) Hide nothing 2) Use nothing 3) rename Int to INT. It is invalid
// because 1) We will show everything 2) Then hide everything 3) Therefore Int is unavailable, error.
import Swift hiding () using () renaming (Int, to: INT)
// 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. 3) Double is unavailable, error.
import Swift using (Int) hiding (String) renaming (Double, to: Triple)
// 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)
Module scope is delimited by the keyword module followed by a fully qualified name and must occur as the first declaration in a file. For example:

// ./Math/Integers/Arithmetic.swift
module Math.Integers.Arithmetic

public protocol _IntegerArithmetic {}

public struct _Abs {}

@_versioned
internal func _abs<Args>(_ args: Args) -> (_Abs, Args) {}

// ./Math/Integers.swift
module Math.Integers

// _abs is visible in this module and all others within the project,
// but is not exported along with it.
internal import Math.Integers.Arithmetic

public protocol IntegerArithmetic : _IntegerArithmetic, Comparable {}
public protocol SignedNumber : Comparable, ExpressibleByIntegerLiteral {}

// Math.swift
module Math

// Exports the entire public contents of Math.Integers, but nothing in
// Math.Integers.Arithmetic.
public import Math.Integers
Modules names are tied to a directory structure that describes their location relative to the current module and it will now be an error to violate this rule. For example:

module String // lives in ./String.swift
module String.Core // lives in ./String/Core.swift
module String.Core.Internals.Do.You.Even.Write // lives in ./String/Core/Internals/Do/You/Even/Write.swift
Existing projects that do not adopt these rules will still retain their implicit module name (usually defined as the name of the framework or application that is being built) and may continue to use whatever directory structure they wish, however they may not declare any explicit modules.

This proposal also solves the problem of module export. A module that is imported without an access level modifier will default to an internal import per usual. However, when it is useful to fully expose the public content of submodules to a client, a public modifier can be used. Similarly, when it is useful to access internal or [file]private APIs, but not expose them to clients, those access modifiers may be used. The rule of thumb is: Only identifiers that are at least as visible as the qualifier on the import make for valid import declarations. For example:

// A submodule declaring a `private` class that gets imported with
// an `internal` qualifier with a `using` directive is an invalid import
// declaration.
module Foo.Bar

private class PrivateThing {}

module Foo

// Error: PrivateThing not visible, use `private import`
import Foo.Bar using (PrivateThing)
// However, a submodule declaring a `public` struct that gets imported with
// an `private` qualifier is a valid import declaration.
module Foo.Bar

public class PublicThing {}

module Foo

// All good! Foo can see Foo.Bar.PrivateThing.
private import Foo.Bar using (PublicThing)
Because import directives are file-local, they will never be exported along with a public import and will default to exporting the entire contents of the module as though you had never declared them.

// In this file and this file alone, the directives apply. To the user
// of this module, it is as though this declaration were simply:
// public import Foundation.Date
public import Foundation.Date hiding (Date.init())
                              renaming (Date.Type.distantPast,
                                        to: Date.Type.letsGoLivingInThePast,
                                        Date.Type.timeIntervalSinceReferenceDate,
                                        to: Date.Type.startOfTheUniverse)
                              renaming (Date.Type.<, to: Date.Type.<<<<<)
<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. Code that is not organized into modules will remain unaffected and organized into one contiguous top-level module. However, it is strongly recommended that frameworks be decomposed and reorganized around the new module system.

As a case study, the public interface to the standard library appears to already be mostly broken down into submodules as described in GroupInfo.json <https://github.com/apple/swift/blob/master/stdlib/public/core/GroupInfo.json>.

Code that is defined in modulemaps already defines a module structure that can be imported directly into this scheme.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#alternatives-considered>Alternatives considered

Module export can also be placed on the module declaration itself. The relevant parts of the grammar that have changed are below with an example:

module-decl -> <access-level-modifier> module <module-path>
import-decl -> import <module-path> <(opt) import-directive-list>
private module String.Core.Internals

// Shh, it's a secret.
While this style makes it immediately obvious to the library author which modules are public or private, it causes the consumer problems because submodule exports are no longer explicit and are entirely ad-hoc. In the interest of enabling, for one, users of IDEs to drill into public submodules, making export local to import seems more appropriate.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Beta) #15

Hello all,

TJ Usiyan, Harlan Haskins, and I have been working on a proposal to rework qualified imports and introduce an explicit module system to Swift that we’d like to publish for your viewing pleasure.

The initial impetus was set out in a radar (rdar://17630570 <rdar://17630570>) I sent fairly early on that didn’t receive a response, so I started a swift-evolution <http://permalink.gmane.org/gmane.comp.lang.swift.evolution/1378> thread discussing the basics of this proposal. It has been refined and expanded a bit to include an effort to make Swift modules explicit and updated with the feedback of that first thread. Contents of the proposal are inline and can also be had as a gist <https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6> or on Github. <https://github.com/apple/swift-evolution/pull/440>

Cheers,

~Robert Widmann

Qualified Imports and Modules

Proposal: SE-NNNN <https://gist.github.com/CodaFi/NNNN-first-class-qualified-imports.md>
Authors: Robert Widmann <https://github.com/codafi>, Harlan Haskins <https://github.com/harlanhaskins>, 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 and the introduction of a module system.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#motivation>Motivation

Swift code is modular by default. However, it is not clear how to decompose existing modules further into submodules. In addition, it is difficult to tell how importing a module affects its export to consumers of a library. This leads many to either fake namespaces with enums, attempt to structure Swift code with modulemaps, or use a large amount of version-control submodules. All of these can be rolled into one complete package in the form of a comprehensive rethink of the qualified import system and the introduction of a module system.

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

Modules will now become an explicit part of working with canonical Swift code. The grammar and semantics of qualified imports will change completely with the addition of import qualifiers and import directives. We also introduce three new contextual keywords: using, hiding, and renaming, 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

module-decl -> module <module-path>
import-decl -> <access-level-modifier> import <module-path> <(opt) import-directive-list>
module-path -> <identifier>
            -> <identifier>.<import-path>
import-directive-list -> <import-directive>
                      -> <import-directive> <import-directive-list>
import-directive -> using (<identifier>, ...)
                 -> hiding (<identifier>, ...)
                 -> renaming (<identifier>, to: <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 3 operations:

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

// The only visible parts of Foundation in this file are
// Date.init(), Date.hashValue, and Date.description.
import Foundation.Date using (Date.init(), Date.hashValue, Date.description)
2) hiding: The hiding directive is followed by a list of identifiers within the imported module that should be hidden from this file.

// Imports all of Foundation.Date except `Date.compare()`
import Foundation.Date hiding (Date.compare())

It's unfortunate that this proposal requires the identifiers to be re-qualified when the imported module is actually the name of a type.

I considered making that a feature of this proposal but I had a tough time reconciling removing the decl specifier from qualified imports and having an unambiguous notation for Swift declarations.

It seems to me that whether e.g. a type is defined in its own sub-module is a detail that users won't really appreciate and which probably shouldn't be surfaced to them. In fact, in general I'm concerned about this turning the file and directory organization of a project into API.

It’s a detail that they’ve already had to have surfaced if they use the existing syntax.

In fact, in general I'm concerned about this turning the file and directory organization of a project into API.

It’s a legitimate concern and one that I share considering there is a way to abuse this restriction (*cough* Java), or try to rally around a limited set of namespaces for common controls.

This proposal also solves the problem of module export. A module that is imported without an access level modifier will default to an internal import per usual. However, when it is useful to fully expose the public content of submodules to a client, a public modifier can be used. Similarly, when it is useful to access internal or [file]private APIs, but not expose them to clients, those access modifiers may be used.

These uses of access modifiers feel inconsistent to me. "public import Foo" exports the contents of Foo as if they were members of my module, but "private import Foo" imports the private APIs (?) of Foo? That is not the same interpretive rule.

It’s not, and it’s that way intentionally. Non-public imports do not re-export. The wording around this is shabby, I’ll work to improve it.

I think the more consistent analogy for "private import" would be to say that the public members of Foo are visible in this file only (i.e. the default behavior of "import" today, which I think we would want to keep), whereas you could do an "internal import" to make the members of Foo visible throughout the current module (potentially useful if you have an interesting set of common modifications you want to make).

I don't know why you think it should be possible to import the private declarations of a module. That seems completely contrary to the access-control design. I agree that it's useful to have sub-modules expose APIs that are only usable by other parts of the larger module, but I think the Swiftier design would be for that to be opt-in on the declaration somehow, or at least to specify how it interacts with "internal".

I was approached by users at WWDC that did wish to have some way of grouping a bunch of private Swift files that should “know about each other’s internal stuff”. At the time this was the semantics that seemed to match that and stayed in-line with what a `private import` could possibly do. Perhaps this kind of import can be banned-by-diagnostic in that case.

Also, it is completely unclear to me why modifiers like "renaming" don't change how the imported module's declarations are re-exported.

Because importing a library shouldn’t be able to change whatever it likes and break client code on re-export. I don’t have a particularly compelling use-case for allowing user-specified mappings to escape file scope and neither do many other languages I can find that permit this feature. If you have one I’d like to know about it.

···

On Jul 18, 2016, at 3:21 PM, John McCall <rjmccall@apple.com> wrote:

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

John.

The rule of thumb is: Only identifiers that are at least as visible as the qualifier on the import make for valid import declarations. For example:

// A submodule declaring a `private` class that gets imported with
// an `internal` qualifier with a `using` directive is an invalid import
// declaration.
module Foo.Bar

private class PrivateThing {}

module Foo

// Error: PrivateThing not visible, use `private import`
import Foo.Bar using (PrivateThing)
// However, a submodule declaring a `public` struct that gets imported with
// an `private` qualifier is a valid import declaration.
module Foo.Bar

public class PublicThing {}

module Foo

// All good! Foo can see Foo.Bar.PrivateThing.
private import Foo.Bar using (PublicThing)
Because import directives are file-local, they will never be exported along with a public import and will default to exporting the entire contents of the module as though you had never declared them.

// In this file and this file alone, the directives apply. To the user
// of this module, it is as though this declaration were simply:
// public import Foundation.Date
public import Foundation.Date hiding (Date.init())
                              renaming (Date.Type.distantPast,
                                        to: Date.Type.letsGoLivingInThePast,
                                        Date.Type.timeIntervalSinceReferenceDate,
                                        to: Date.Type.startOfTheUniverse)
                              renaming (Date.Type.<, to: Date.Type.<<<<<)
<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. Code that is not organized into modules will remain unaffected and organized into one contiguous top-level module. However, it is strongly recommended that frameworks be decomposed and reorganized around the new module system.

As a case study, the public interface to the standard library appears to already be mostly broken down into submodules as described in GroupInfo.json <https://github.com/apple/swift/blob/master/stdlib/public/core/GroupInfo.json>.

Code that is defined in modulemaps already defines a module structure that can be imported directly into this scheme.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#alternatives-considered>Alternatives considered

Module export can also be placed on the module declaration itself. The relevant parts of the grammar that have changed are below with an example:

module-decl -> <access-level-modifier> module <module-path>
import-decl -> import <module-path> <(opt) import-directive-list>
private module String.Core.Internals

// Shh, it's a secret.
While this style makes it immediately obvious to the library author which modules are public or private, it causes the consumer problems because submodule exports are no longer explicit and are entirely ad-hoc. In the interest of enabling, for one, users of IDEs to drill into public submodules, making export local to import seems more appropriate.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Xiaodi Wu) #16

This is an interesting document. I think it deserves careful study. For
now, some questions:

What is the rationale behind permitting the using of specific methods?
This seems to be usually fine-grained in comparison to other languages.
What use cases do you have in mind for this?

One use case: Swift libraries export not just member references as I’ve
used here, but a large amount of free functions. It has long been a
problem that free functions seem to pollute a shared namespace and there
didn’t seem to be a clear way to hide them.

Would a plausible simplification of the proposal be to have it fine-grained
enough to address free functions but not methods inside types?
Incidentally, although I do not see it in the proposal, I assume that * in
some form will be permitted (as in, `import Foundation using *`).

I can see the use case for hiding specific symbols when they come into
conflict with your own, but in your example you're hiding specific methods
declared *in* an imported type. What is the use case here? Is it going to
allow me to open backdoors so that, if I don't like `Foo.frobnicate()`, I
can hide it and then substitute my own in an extension? This seems like a
bad thing at first blush.

For members that would be an acceptable use-case. The worst-case scenario
that comes to mind is this being used as a way to “virtually override” a
method in a subclass. Then again, the scope of the damage is limited to
the file in which you’ve declared this monstrosity so clients and even you
will not be able to see it outside of there unless you explicitly redeclare
the hiding import (in which case, you probably know what you’re doing).

A use care here might be hiding the KVO-ish parts of an object from
yourself, or more generally subsetting out the part of an API you know you
shouldn’t interact with in a particular submodule.

I can see the obvious use case for renaming modules and types on
import--basically, in my mind, it's like typealiases with hiding, and it's
available in other languages of course. But how would renaming methods
work? If Foo conforms to Equatable and I rename `Foo.==` to `Foo.!=`, is
the type I import still Equatable? How would it behave? And even if Foo is
fine, what happens if I try to subclass my Frankensteinian Foo?

Of course you still conform to Equatable. The renaming defines a mapping
from your names to “proper" names. For example, if you use a renaming
import to change the requirements of a protocol in a file, then your
conformance will simply look at the mapping and see that everything
resolves into its proper place. Bear in mind that your renamings will not
survive outside of the file in which you declare them. Frankenteinian Foo
exists where you say it does and nowhere else. Everybody else just sees
Foo conform to Equatable (unless they rename things themselves).

Maybe let's work through an example:

Suppose we have in stdlib:

public protocol FooProtocol {
  func frobnicate()
}

Now, I write a library:

import Swift.FooProtocol renaming (FooProtocol.frobnicate(), to:
FooProtocol.bobnicate())

public open class MyFoo : Swift.FooProtocol {
  public open func bobnicate() {
    print("Does your head hurt yet?")
  }
}

Now, you are an end user of my sinister library.

What is the public API of `MyFoo`?
For you, does `MyFoo` conform to `Swift.FooProtocol`?
Can you call `MyFoo.frobnicate()`? How about `MyFoo.bobnicate()`?

What if you try to subclass `MyFoo`?
Does your subclass still conform to `Swift.FooProtocol`?
Do you override `bobnicate()` or `frobnicate()`?
My head hurts...

···

On Mon, Jul 18, 2016 at 4:49 PM, Robert Widmann <rwidmann@apple.com> wrote:

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

On Mon, Jul 18, 2016 at 16:10 Robert Widmann via swift-evolution < > swift-evolution@swift.org> wrote:

Hello all,

TJ Usiyan, Harlan Haskins, and I have been working on a proposal to
rework qualified imports and introduce an explicit module system to Swift
that we’d like to publish for your viewing pleasure.

The initial impetus was set out in a radar (rdar://17630570) I sent
fairly early on that didn’t receive a response, so I started a
swift-evolution
<http://permalink.gmane.org/gmane.comp.lang.swift.evolution/1378> thread
discussing the basics of this proposal. It has been refined and expanded a
bit to include an effort to make Swift modules explicit and updated with
the feedback of that first thread. Contents of the proposal are inline and can
also be had as a gist
<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6> or on
Github. <https://github.com/apple/swift-evolution/pull/440>

Cheers,

~Robert Widmann

Qualified Imports and Modules

   - Proposal: SE-NNNN
   <https://gist.github.com/CodaFi/NNNN-first-class-qualified-imports.md>
   - Authors: Robert Widmann <https://github.com/codafi>, Harlan Haskins
   <https://github.com/harlanhaskins>, 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 and the introduction of a module system.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#motivation>
Motivation

Swift code is modular by default. However, it is not clear how to
decompose existing modules further into submodules. In addition, it is
difficult to tell how importing a module affects its export to consumers of
a library. This leads many to either fake namespaces with enums, attempt to
structure Swift code with modulemaps, or use a large amount of
version-control submodules. All of these can be rolled into one complete
package in the form of a comprehensive rethink of the qualified import
system and the introduction of a module system.

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

Modules will now become an explicit part of working with canonical Swift
code. The grammar and semantics of qualified imports will change completely
with the addition of *import qualifiers* and *import directives*. We
also introduce three new contextual keywords: using, hiding, and renaming,
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

module-decl -> module <module-path>
import-decl -> <access-level-modifier> import <module-path> <(opt) import-directive-list>
module-path -> <identifier>
            -> <identifier>.<import-path>
import-directive-list -> <import-directive>
                      -> <import-directive> <import-directive-list>
import-directive -> using (<identifier>, ...)
                 -> hiding (<identifier>, ...)
                 -> renaming (<identifier>, to: <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 3 operations:

1) *using*: The *using* directive is followed by a list of identifiers
within the imported module that should be exposed to this file.

// The only visible parts of Foundation in this file are // Date.init(), Date.hashValue, and Date.description.import Foundation.Date using (Date.init(), Date.hashValue, Date.description)

2) *hiding*: The hiding directive is followed by a list of identifiers
within the imported module that should be hidden from this file.

// Imports all of Foundation.Date except `Date.compare()`import Foundation.Date hiding (Date.compare())

3) *renaming*: The renaming directive is followed by a list of
identifiers separated by to: that should be exposed to this file but
renamed.

// Imports all of Dispatch.DispatchQueue but renames the static member // DispatchQueue.main, to DispatchQueue.mainQueueimport Dispatch.DispatchQueue renaming (DispatchQueue.Type.main to: DispatchQueue.Type.mainQueue)// Renaming can also rename modules. All members of UIKit have to be qualified with// `UI` now.import UIKit renaming (UIKit, to: UI)

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

// Imports all of Foundation except `DateFormatter` and renames `Cache` to `LRUCache`import Foundation hiding (DateFormatter) renaming (Cache to: LRUCache)// Imports SCNNode except SCNNode.init(mdlObject:) and renames `.description` to// `.nodeDescription` import SceneKit using (SCNNode)
                renaming (SCNNode.description, to: SCNNode.nodeDescription)
                hiding (SCNNode.init(mdlObject:))

Directive chaining occurs left-to-right:

// This says to 1) Hide nothing 2) Use nothing 3) rename Int to INT. It is invalid// because 1) We will show everything 2) Then hide everything 3) Therefore Int is unavailable, error.import Swift hiding () using () renaming (Int, to: INT)// 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. 3) Double is unavailable, error.import Swift using (Int) hiding (String) renaming (Double, to: Triple)// 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)

Module scope is delimited by the keyword module followed by a fully
qualified name and must occur as the first declaration in a file. For
example:

// ./Math/Integers/Arithmetic.swift
module Math.Integers.Arithmetic
public protocol _IntegerArithmetic {}
public struct _Abs {}
@_versionedinternal func _abs<Args>(_ args: Args) -> (_Abs, Args) {}
// ./Math/Integers.swift
module Math.Integers
// _abs is visible in this module and all others within the project, // but is not exported along with it.internal import Math.Integers.Arithmetic
public protocol IntegerArithmetic : _IntegerArithmetic, Comparable {}public protocol SignedNumber : Comparable, ExpressibleByIntegerLiteral {}

// Math.swift
module Math
// Exports the entire public contents of Math.Integers, but nothing in // Math.Integers.Arithmetic.public import Math.Integers

Modules names are tied to a directory structure that describes their
location relative to the current module and it will now be an error to
violate this rule. For example:

module String // lives in ./String.swift
module String.Core // lives in ./String/Core.swift
module String.Core.Internals.Do.You.Even.Write // lives in ./String/Core/Internals/Do/You/Even/Write.swift

Existing projects that do not adopt these rules will still retain their *implicit
module name* (usually defined as the name of the framework or
application that is being built) and may continue to use whatever directory
structure they wish, however they may not declare any explicit modules.

This proposal also solves the problem of module *export*. A module that
is imported without an access level modifier will default to an internal import
per usual. However, when it is useful to fully expose the public content of
submodules to a client, a public modifier can be used. Similarly, when
it is useful to access internal or [file]private APIs, but not expose
them to clients, those access modifiers may be used. The rule of thumb is:
Only identifiers that are at least as visible as the qualifier on the
import make for valid import declarations. For example:

// A submodule declaring a `private` class that gets imported with // an `internal` qualifier with a `using` directive is an invalid import // declaration.
module Foo.Bar
private class PrivateThing {}

module Foo
// Error: PrivateThing not visible, use `private import`import Foo.Bar using (PrivateThing)

// However, a submodule declaring a `public` struct that gets imported with // an `private` qualifier is a valid import declaration.
module Foo.Bar
public class PublicThing {}

module Foo
// All good! Foo can see Foo.Bar.PrivateThing.private import Foo.Bar using (PublicThing)

Because import directives are file-local, they will never be exported
along with a public import and will default to exporting the entire
contents of the module as though you had never declared them.

// In this file and this file alone, the directives apply. To the user// of this module, it is as though this declaration were simply:// public import Foundation.Datepublic import Foundation.Date hiding (Date.init())
                              renaming (Date.Type.distantPast,
                                        to: Date.Type.letsGoLivingInThePast,
                                        Date.Type.timeIntervalSinceReferenceDate,
                                        to: Date.Type.startOfTheUniverse)
                              renaming (Date.Type.<, to: Date.Type.<<<<<)

<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. Code that is not organized into modules will remain
unaffected and organized into one contiguous top-level module. However, it
is strongly recommended that frameworks be decomposed and reorganized
around the new module system.

As a case study, the public interface to the standard library appears to
already be mostly broken down into submodules as described in
GroupInfo.json
<https://github.com/apple/swift/blob/master/stdlib/public/core/GroupInfo.json>
.

Code that is defined in modulemaps already defines a module structure
that can be imported directly into this scheme.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#alternatives-considered>Alternatives
considered

Module export can also be placed on the module declaration itself. The
relevant parts of the grammar that have changed are below with an example:

module-decl -> <access-level-modifier> module <module-path>
import-decl -> import <module-path> <(opt) import-directive-list>

private module String.Core.Internals
// Shh, it's a secret.

While this style makes it immediately obvious to the library author which
modules are public or private, it causes the consumer problems because
submodule exports are no longer explicit and are entirely ad-hoc. In the
interest of enabling, for one, users of IDEs to drill into public
submodules, making export local to import seems more appropriate.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Beta) #17

Hello all,

TJ Usiyan, Harlan Haskins, and I have been working on a proposal to rework qualified imports and introduce an explicit module system to Swift that we’d like to publish for your viewing pleasure.

This is really interesting. A few incomplete comments:

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

// Imports all of Foundation.Date except `Date.compare()`
import Foundation.Date hiding (Date.compare())

[snip]

// Exports the entire public contents of Math.Integers, but nothing in
// Math.Integers.Arithmetic.
public import Math.Integers

Would this work?

  module UIKit
  
  public import UIKit.UIGestureRecognizerSubclass.UIGestureRecognizer
    hiding (UIGestureRecognizer.touchesBegan(_:with:), UIGestureRecognizer.touchesMoved(_:with:), …)

(If so, we'd need a way to hide only the setter of a property, not the getter.)

Yes, that would work. Notation for hiding setters and getters through the import system (probably best to do it internally, but I see what you mean) can ideally be another dot qualifier

public import Foo hiding (Foo.bar) // Hide the whole thing
public import Foo hiding (Foo.bar.set) // Hide the setter.
public import Foo hiding (Foo.bar.get) // Hide the getter [Not sure why you’d want this].

Modules names are tied to a directory structure that describes their location relative to the current module and it will now be an error to violate this rule. For example:

module String // lives in ./String.swift

module String.Core // lives in ./String/Core.swift

module String.Core.Internals.Do.You.Even.Write // lives in ./String/Core/Internals/Do/You/Even/Write.swift

I think this is a mistake for several reasons:

* You may need to split a single submodule across multiple files, but this rule doesn't allow that.

* The module declaration and filename contain redundant information, and one of them could get out of sync with the other.

* Xcode doesn't like to organize things into folders on disk and will fight you tooth and nail.

* Deeply nested folders are a usability issue. Never forget the jury in Oracle v. Google: https://www.geek.com/wp-content/uploads/2016/05/courtroomhijinks.png

At the very least, I would like to see allowances for multi-file submodules—String/Core/Internals/Do/You/Even/Write**.swift. Better would be to use long filenames—String.Core.Internals.Do.You.Even.Write*.swift. Even better would be to just allow freeform naming and trust programmers to organize their projects sanely.

We are not proposing a Java-style module system so much as an extension of the existing one to submodules the way they are used today for frameworks like Darwin. Projects no longer require reverse-DNS-style directory structures and nesting of submodules to great depth can grow unwieldy, but that may be a sign that a project is growing too decentralized. Large frameworks will decompose themselves into submodules in roughly the same way that projects written in Objective-C, C, C++ have always decomposed themselves. The directory structure for that style of framework rarely grows to the extend you’re concerned with here.

Existing projects that do not adopt these rules will still retain their implicit module name (usually defined as the name of the framework or application that is being built) and may continue to use whatever directory structure they wish, however they may not declare any explicit modules.

This proposal also solves the problem of module export. A module that is imported without an access level modifier will default to an internal import per usual. However, when it is useful to fully expose the public content of submodules to a client, a public modifier can be used. Similarly, when it is useful to access internal or [file]private APIs, but not expose them to clients, those access modifiers may be used. The rule of thumb is: Only identifiers that are at least as visible as the qualifier on the import make for valid import declarations.

It appears the semantics here are "grab all declarations at level X and above, and expose them in this module as level X". This is bizarre for a number of reasons:

* Currently, `import` can only access `internal` declarations using `@testable`. This would change that.

It would not. Re-export can only re-export public APIs.

* Currently, `import` can't access `private` and `fileprivate` declarations at all, and it's not clear what it would mean to add that. What happens if two different parts of the module have different `private` members with the same name? Which do you get?

If we decide to allow this you will get an ambiguity which you can resolve with a renaming. John has made me reconsider the semantics of a private import.

* Currently, `import` only affects the current file—it's effectively "import `public` members as `fileprivate`". If your default is `internal import`, that would imply that an import statement in one file would, by default, expose the APIs it imported to all files. That's an important change in behavior.

It changes existing behavior in the sense that internal imports are now module-scoped rather than project-scoped but you still cannot re-export non-public APIs. I specifically want to *remove* the behavior where an imported API winds up recursively importing submodules until your qualified import is just garbage anyway. Your submodules will not see an `internal import` in the same way that a project can currently see an internal import unless they themselves declare an `internal import` of that module. And if they’re re-exporting public APIs then you probably wanted to see that when you imported them anyway.

I think you're mixing two things together that ought not to be. `import` should always import only public APIs (unless you use `@testable`—which might need a bit of renaming to support the additional use case of SPIs between submodules and supermodules—in which case you also get `internal` APIs).

And it does.

An access modifier on the `import` statement controls how they're exposed to the rest of the file/project/world, and `private` is the default. It's a little weird to have `private` be the default on `import` when `internal` is the default on everything else, but the alternative is to change `import`'s behavior in a way that is neither backwards-compatible, nor likely to be correct.

Internal is the default.

This proposal also solves the problem of module export.
A module that is imported without an access level modifier will default to an internal import per usual.

Doesn’t mean you can import internal APIs from modules you don’t own (that’s still banned). You can still only export public API.

···

On Jul 18, 2016, at 4:34 PM, Brent Royal-Gordon <brent@architechies.com> wrote:

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

Because import directives are file-local, they will never be exported along with a public import and will default to exporting the entire contents of the module as though you had never declared them.

Whoa, wait, what? Why? I think hiding parts of the implementation is a good use case for re-exporting a module. And I think the clear implication of attaching these clauses directly to the import statement is that it controls how the module is imported everywhere that statement makes it visible, not just within the one file. If we're not going to do that, I think these clauses ought to be separated from the `import` statement and turned into something separate.

--
Brent Royal-Gordon
Architechies


(Xiaodi Wu) #18

This is an interesting document. I think it deserves careful study. For
now, some questions:

What is the rationale behind permitting the using of specific methods?
This seems to be usually fine-grained in comparison to other languages.
What use cases do you have in mind for this?

One use case: Swift libraries export not just member references as I’ve
used here, but a large amount of free functions. It has long been a
problem that free functions seem to pollute a shared namespace and there
didn’t seem to be a clear way to hide them.

Would a plausible simplification of the proposal be to have it
fine-grained enough to address free functions but not methods inside types?
Incidentally, although I do not see it in the proposal, I assume that * in
some form will be permitted (as in, `import Foundation using *`).

I can see the use case for hiding specific symbols when they come into
conflict with your own, but in your example you're hiding specific methods
declared *in* an imported type. What is the use case here? Is it going to
allow me to open backdoors so that, if I don't like `Foo.frobnicate()`, I
can hide it and then substitute my own in an extension? This seems like a
bad thing at first blush.

For members that would be an acceptable use-case. The worst-case
scenario that comes to mind is this being used as a way to “virtually
override” a method in a subclass. Then again, the scope of the damage is
limited to the file in which you’ve declared this monstrosity so clients
and even you will not be able to see it outside of there unless you
explicitly redeclare the hiding import (in which case, you probably know
what you’re doing).

A use care here might be hiding the KVO-ish parts of an object from
yourself, or more generally subsetting out the part of an API you know you
shouldn’t interact with in a particular submodule.

I can see the obvious use case for renaming modules and types on
import--basically, in my mind, it's like typealiases with hiding, and it's
available in other languages of course. But how would renaming methods
work? If Foo conforms to Equatable and I rename `Foo.==` to `Foo.!=`, is
the type I import still Equatable? How would it behave? And even if Foo is
fine, what happens if I try to subclass my Frankensteinian Foo?

Of course you still conform to Equatable. The renaming defines a mapping
from your names to “proper" names. For example, if you use a renaming
import to change the requirements of a protocol in a file, then your
conformance will simply look at the mapping and see that everything
resolves into its proper place. Bear in mind that your renamings will not
survive outside of the file in which you declare them. Frankenteinian Foo
exists where you say it does and nowhere else. Everybody else just sees
Foo conform to Equatable (unless they rename things themselves).

Maybe let's work through an example:

Suppose we have in stdlib:

public protocol FooProtocol {
  func frobnicate()
}

Now, I write a library:

import Swift.FooProtocol renaming (FooProtocol.frobnicate(), to:
FooProtocol.bobnicate())

public open class MyFoo : Swift.FooProtocol {
  public open func bobnicate() {
    print("Does your head hurt yet?")
  }
}

Now, you are an end user of my sinister library.

What is the public API of `MyFoo`?

The proposal addresses this

> Because import directives are file-local, they will never be exported
along with a `public` import and will
> default to exporting the entire contents of the module as though you had
never declared them.

The user (and even you in other files that import this module) will see a
protocol conformance exactly as laid out in the Swift.FooProtocol module.

For you, does `MyFoo` conform to `Swift.FooProtocol`?

It conforms because the renaming you wrote describes a way of resolving
FooProtocol.bobnicate() (your API) to FooProtocol.frobnicate() (everybody
else’s API).

Can you call `MyFoo.frobnicate()`? How about `MyFoo.bobnicate()`?

What if you try to subclass `MyFoo`?

If you are inside the module you wrote the renaming, you will use it. If
you are outside of it, you will see the protocol requirement sans renaming.

Does your subclass still conform to `Swift.FooProtocol`?
Do you override `bobnicate()` or `frobnicate()`?
My head hurts…

Because you have explicitly renamed the protocol requirement, you will
override the same protocol requirement both inside and outside this module
but your renaming will not propagate to other files unless they themselves
opt in the way you have here. It would be particularly sinister if you
could arbitrarily edit the user-facing API of members simply by importing a
library.

Sounds good. If I understand you correctly, by conforming `MyFoo` to an
internally renamed `Swift.FooProtocol`, the renaming of the user-facing API
for `FooProtocol` means that the public API of `MyFoo` is changed so that,
outside the module, it has a member `frobnicate()` and no longer has a
member `bobnicate()`?

···

On Mon, Jul 18, 2016 at 5:09 PM, Robert Widmann <rwidmann@apple.com> wrote:

On Jul 18, 2016, at 3:00 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Mon, Jul 18, 2016 at 4:49 PM, Robert Widmann <rwidmann@apple.com> > wrote:

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

On Mon, Jul 18, 2016 at 16:10 Robert Widmann via swift-evolution < >> swift-evolution@swift.org> wrote:

Hello all,

TJ Usiyan, Harlan Haskins, and I have been working on a proposal to
rework qualified imports and introduce an explicit module system to Swift
that we’d like to publish for your viewing pleasure.

The initial impetus was set out in a radar (rdar://17630570) I sent
fairly early on that didn’t receive a response, so I started a
swift-evolution
<http://permalink.gmane.org/gmane.comp.lang.swift.evolution/1378> thread
discussing the basics of this proposal. It has been refined and expanded a
bit to include an effort to make Swift modules explicit and updated with
the feedback of that first thread. Contents of the proposal are inline and can
also be had as a gist
<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6> or on
Github. <https://github.com/apple/swift-evolution/pull/440>

Cheers,

~Robert Widmann

Qualified Imports and Modules

   - Proposal: SE-NNNN
   <https://gist.github.com/CodaFi/NNNN-first-class-qualified-imports.md>
   - Authors: Robert Widmann <https://github.com/codafi>, Harlan Haskins
   <https://github.com/harlanhaskins>, 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 and the introduction of a module system.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#motivation>
Motivation

Swift code is modular by default. However, it is not clear how to
decompose existing modules further into submodules. In addition, it is
difficult to tell how importing a module affects its export to consumers of
a library. This leads many to either fake namespaces with enums, attempt to
structure Swift code with modulemaps, or use a large amount of
version-control submodules. All of these can be rolled into one complete
package in the form of a comprehensive rethink of the qualified import
system and the introduction of a module system.

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

Modules will now become an explicit part of working with canonical Swift
code. The grammar and semantics of qualified imports will change completely
with the addition of *import qualifiers* and *import directives*. We
also introduce three new contextual keywords: using, hiding, and
renaming, 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

module-decl -> module <module-path>
import-decl -> <access-level-modifier> import <module-path> <(opt) import-directive-list>
module-path -> <identifier>
            -> <identifier>.<import-path>
import-directive-list -> <import-directive>
                      -> <import-directive> <import-directive-list>
import-directive -> using (<identifier>, ...)
                 -> hiding (<identifier>, ...)
                 -> renaming (<identifier>, to: <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 3 operations:

1) *using*: The *using* directive is followed by a list of identifiers
within the imported module that should be exposed to this file.

// The only visible parts of Foundation in this file are // Date.init(), Date.hashValue, and Date.description.import Foundation.Date using (Date.init(), Date.hashValue, Date.description)

2) *hiding*: The hiding directive is followed by a list of identifiers
within the imported module that should be hidden from this file.

// Imports all of Foundation.Date except `Date.compare()`import Foundation.Date hiding (Date.compare())

3) *renaming*: The renaming directive is followed by a list of
identifiers separated by to: that should be exposed to this file but
renamed.

// Imports all of Dispatch.DispatchQueue but renames the static member // DispatchQueue.main, to DispatchQueue.mainQueueimport Dispatch.DispatchQueue renaming (DispatchQueue.Type.main to: DispatchQueue.Type.mainQueue)// Renaming can also rename modules. All members of UIKit have to be qualified with// `UI` now.import UIKit renaming (UIKit, to: UI)

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

// Imports all of Foundation except `DateFormatter` and renames `Cache` to `LRUCache`import Foundation hiding (DateFormatter) renaming (Cache to: LRUCache)// Imports SCNNode except SCNNode.init(mdlObject:) and renames `.description` to// `.nodeDescription` import SceneKit using (SCNNode)
                renaming (SCNNode.description, to: SCNNode.nodeDescription)
                hiding (SCNNode.init(mdlObject:))

Directive chaining occurs left-to-right:

// This says to 1) Hide nothing 2) Use nothing 3) rename Int to INT. It is invalid// because 1) We will show everything 2) Then hide everything 3) Therefore Int is unavailable, error.import Swift hiding () using () renaming (Int, to: INT)// 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. 3) Double is unavailable, error.import Swift using (Int) hiding (String) renaming (Double, to: Triple)// 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)

Module scope is delimited by the keyword module followed by a fully
qualified name and must occur as the first declaration in a file. For
example:

// ./Math/Integers/Arithmetic.swift
module Math.Integers.Arithmetic
public protocol _IntegerArithmetic {}
public struct _Abs {}
@_versionedinternal func _abs<Args>(_ args: Args) -> (_Abs, Args) {}
// ./Math/Integers.swift
module Math.Integers
// _abs is visible in this module and all others within the project, // but is not exported along with it.internal import Math.Integers.Arithmetic
public protocol IntegerArithmetic : _IntegerArithmetic, Comparable {}public protocol SignedNumber : Comparable, ExpressibleByIntegerLiteral {}

// Math.swift
module Math
// Exports the entire public contents of Math.Integers, but nothing in // Math.Integers.Arithmetic.public import Math.Integers

Modules names are tied to a directory structure that describes their
location relative to the current module and it will now be an error to
violate this rule. For example:

module String // lives in ./String.swift
module String.Core // lives in ./String/Core.swift
module String.Core.Internals.Do.You.Even.Write // lives in ./String/Core/Internals/Do/You/Even/Write.swift

Existing projects that do not adopt these rules will still retain their *implicit
module name* (usually defined as the name of the framework or
application that is being built) and may continue to use whatever directory
structure they wish, however they may not declare any explicit modules.

This proposal also solves the problem of module *export*. A module that
is imported without an access level modifier will default to an internal import
per usual. However, when it is useful to fully expose the public content of
submodules to a client, a public modifier can be used. Similarly, when
it is useful to access internal or [file]private APIs, but not expose
them to clients, those access modifiers may be used. The rule of thumb is:
Only identifiers that are at least as visible as the qualifier on the
import make for valid import declarations. For example:

// A submodule declaring a `private` class that gets imported with // an `internal` qualifier with a `using` directive is an invalid import // declaration.
module Foo.Bar
private class PrivateThing {}

module Foo
// Error: PrivateThing not visible, use `private import`import Foo.Bar using (PrivateThing)

// However, a submodule declaring a `public` struct that gets imported with // an `private` qualifier is a valid import declaration.
module Foo.Bar
public class PublicThing {}

module Foo
// All good! Foo can see Foo.Bar.PrivateThing.private import Foo.Bar using (PublicThing)

Because import directives are file-local, they will never be exported
along with a public import and will default to exporting the entire
contents of the module as though you had never declared them.

// In this file and this file alone, the directives apply. To the user// of this module, it is as though this declaration were simply:// public import Foundation.Datepublic import Foundation.Date hiding (Date.init())
                              renaming (Date.Type.distantPast,
                                        to: Date.Type.letsGoLivingInThePast,
                                        Date.Type.timeIntervalSinceReferenceDate,
                                        to: Date.Type.startOfTheUniverse)
                              renaming (Date.Type.<, to: Date.Type.<<<<<)

<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. Code that is not organized into modules will remain
unaffected and organized into one contiguous top-level module. However, it
is strongly recommended that frameworks be decomposed and reorganized
around the new module system.

As a case study, the public interface to the standard library appears to
already be mostly broken down into submodules as described in
GroupInfo.json
<https://github.com/apple/swift/blob/master/stdlib/public/core/GroupInfo.json>
.

Code that is defined in modulemaps already defines a module structure
that can be imported directly into this scheme.

<https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#alternatives-considered>Alternatives
considered

Module export can also be placed on the module declaration itself. The
relevant parts of the grammar that have changed are below with an example:

module-decl -> <access-level-modifier> module <module-path>
import-decl -> import <module-path> <(opt) import-directive-list>

private module String.Core.Internals
// Shh, it's a secret.

While this style makes it immediately obvious to the library author
which modules are public or private, it causes the consumer problems
because submodule exports are no longer explicit and are entirely ad-hoc.
In the interest of enabling, for one, users of IDEs to drill into public
submodules, making export local to import seems more appropriate.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(TJ Usiyan) #19

> Modules names are tied to a directory structure that describes their
location relative to the current module and it will now be an error to
violate this rule. For example:
>
> module String // lives in ./String.swift
>
> module String.Core // lives in ./String/Core.swift
>
> module String.Core.Internals.Do.You.Even.Write // lives in
./String/Core/Internals/Do/You/Even/Write.swift
I think this is a mistake for several reasons:
* You may need to split a single submodule across multiple files, but this
rule doesn't allow that.

You can, internally, have several modules and then create one that imports
those smaller ones publicly with the name that you desire.

* The module declaration and filename contain redundant information, and
one of them could get out of sync with the other.

I agree with this point though I am not sure that it is so much of an
issue. Diagnostics can handle much of the issue.

···

* Xcode doesn't like to organize things into folders on disk and will
fight you tooth and nail.

* Deeply nested folders are a usability issue. Never forget the jury in
Oracle v. Google:
https://www.geek.com/wp-content/uploads/2016/05/courtroomhijinks.png

On Mon, Jul 18, 2016 at 7:34 PM, Brent Royal-Gordon via swift-evolution < swift-evolution@swift.org> wrote:

> On Jul 18, 2016, at 2:09 PM, Robert Widmann via swift-evolution < > swift-evolution@swift.org> wrote:
>
> Hello all,
>
> TJ Usiyan, Harlan Haskins, and I have been working on a proposal to
rework qualified imports and introduce an explicit module system to Swift
that we’d like to publish for your viewing pleasure.

This is really interesting. A few incomplete comments:

> 2) hiding: The hiding directive is followed by a list of identifiers
within the imported module that should be hidden from this file.
>
> // Imports all of Foundation.Date except `Date.compare()`
> import Foundation.Date hiding (Date.compare())
[snip]
> // Exports the entire public contents of Math.Integers, but nothing in
> // Math.Integers.Arithmetic.
> public import Math.Integers

Would this work?

        module UIKit

        public import UIKit.UIGestureRecognizerSubclass.UIGestureRecognizer
                hiding (UIGestureRecognizer.touchesBegan(_:with:),
UIGestureRecognizer.touchesMoved(_:with:), …)

(If so, we'd need a way to hide only the setter of a property, not the
getter.)

> Modules names are tied to a directory structure that describes their
location relative to the current module and it will now be an error to
violate this rule. For example:
>
> module String // lives in ./String.swift
>
> module String.Core // lives in ./String/Core.swift
>
> module String.Core.Internals.Do.You.Even.Write // lives in
./String/Core/Internals/Do/You/Even/Write.swift

I think this is a mistake for several reasons:

* You may need to split a single submodule across multiple files, but this
rule doesn't allow that.

* The module declaration and filename contain redundant information, and
one of them could get out of sync with the other.

* Xcode doesn't like to organize things into folders on disk and will
fight you tooth and nail.

* Deeply nested folders are a usability issue. Never forget the jury in
Oracle v. Google:
https://www.geek.com/wp-content/uploads/2016/05/courtroomhijinks.png

At the very least, I would like to see allowances for multi-file
submodules—String/Core/Internals/Do/You/Even/Write**.swift. Better would be
to use long filenames—String.Core.Internals.Do.You.Even.Write*.swift. Even
better would be to just allow freeform naming and trust programmers to
organize their projects sanely.

> Existing projects that do not adopt these rules will still retain their
implicit module name (usually defined as the name of the framework or
application that is being built) and may continue to use whatever directory
structure they wish, however they may not declare any explicit modules.
>
> This proposal also solves the problem of module export. A module that is
imported without an access level modifier will default to an internal
import per usual. However, when it is useful to fully expose the public
content of submodules to a client, a public modifier can be used.
Similarly, when it is useful to access internal or [file]private APIs, but
not expose them to clients, those access modifiers may be used. The rule of
thumb is: Only identifiers that are at least as visible as the qualifier on
the import make for valid import declarations.

It appears the semantics here are "grab all declarations at level X and
above, and expose them in this module as level X". This is bizarre for a
number of reasons:

* Currently, `import` can only access `internal` declarations using
`@testable`. This would change that.

* Currently, `import` can't access `private` and `fileprivate`
declarations at all, and it's not clear what it would mean to add that.
What happens if two different parts of the module have different `private`
members with the same name? Which do you get?

* Currently, `import` only affects the current file—it's effectively
"import `public` members as `fileprivate`". If your default is `internal
import`, that would imply that an import statement in one file would, by
default, expose the APIs it imported to all files. That's an important
change in behavior.

I think you're mixing two things together that ought not to be. `import`
should always import only public APIs (unless you use `@testable`—which
might need a bit of renaming to support the additional use case of SPIs
between submodules and supermodules—in which case you also get `internal`
APIs). An access modifier on the `import` statement controls how they're
exposed to the rest of the file/project/world, and `private` is the
default. It's a little weird to have `private` be the default on `import`
when `internal` is the default on everything else, but the alternative is
to change `import`'s behavior in a way that is neither
backwards-compatible, nor likely to be correct.

> Because import directives are file-local, they will never be exported
along with a public import and will default to exporting the entire
contents of the module as though you had never declared them.

Whoa, wait, what? Why? I think hiding parts of the implementation is a
good use case for re-exporting a module. And I think the clear implication
of attaching these clauses directly to the import statement is that it
controls how the module is imported everywhere that statement makes it
visible, not just within the one file. If we're not going to do that, I
think these clauses ought to be separated from the `import` statement and
turned into something separate.

--
Brent Royal-Gordon
Architechies

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


(Félix Cloutier) #20

My gut feeling is also that this is more complex than it needs to be. If we want to keep all of these features around, I'd be in favor of looking for a syntax that "flows better" than three separate clauses.

Also, under the current model (or a somewhat recent model, because I ran into that last month), everything included in a bridging header is implicitly imported into every Swift file. If it #includes Foundation, you won't get to refine that from Swift. The proposal would probably benefit from taking that into account, somehow. (Allow re-imports? Separate module imports from using-like statements?)

Finally, should selecting extension methods with the same name or conformances to the same protocol from different modules be considered a problem that this proposal intends to solve?

Félix

···

Le 18 juil. 2016 à 18:19:59, Joe Groff via swift-evolution <swift-evolution@swift.org> a écrit :

Our import story definitely needs work, and this is a step in the right direction. Thanks for working on this! Some comments:

- The import changes can be separated from the submodule issues. Enhancing imports is IMO more important, and is source-breaking today, whereas 'module ' declarations and submodules can be added later. I'd suggest breaking this into two proposals.
- I think the `import` design you propose is a bit more complicated than it needs to be. Python and Haskell get by just having "import everything" and "import itemized (with aliases)". I don't see the need for 'hiding'; if you have a rule that itemized imports get priority over import-everything, then that covers the most important use case of selectively shadowing one module's imports with another. Bikeshed-wise, I don't see much reason to veer from the Java/Haskell-ish template of:

import Foo.* // import everything from module Foo
import Foo.(x, y, z as zed) // import x, y, and z from foo, renaming Foo.z to zed

-Joe

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

Hello all,

TJ Usiyan, Harlan Haskins, and I have been working on a proposal to rework qualified imports and introduce an explicit module system to Swift that we’d like to publish for your viewing pleasure.

The initial impetus was set out in a radar (rdar://17630570) I sent fairly early on that didn’t receive a response, so I started a swift-evolution thread discussing the basics of this proposal. It has been refined and expanded a bit to include an effort to make Swift modules explicit and updated with the feedback of that first thread. Contents of the proposal are inline and can also be had as a gist or on Github.

Cheers,

~Robert Widmann

Qualified Imports and Modules

  • Proposal: SE-NNNN
  • Authors: Robert Widmann, Harlan Haskins, TJ Usiyan
  • Status: Awaiting review
  • Review manager: TBD
Introduction

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

Motivation

Swift code is modular by default. However, it is not clear how to decompose existing modules further into submodules. In addition, it is difficult to tell how importing a module affects its export to consumers of a library. This leads many to either fake namespaces with enums, attempt to structure Swift code with modulemaps, or use a large amount of version-control submodules. All of these can be rolled into one complete package in the form of a comprehensive rethink of the qualified import system and the introduction of a module system.

Proposed solution

Modules will now become an explicit part of working with canonical Swift code. The grammar and semantics of qualified imports will change completely with the addition of import qualifiers and import directives. We also introduce three new contextual keywords: using, hiding, and renaming, to facilitate fine-grained usage of module contents.

Detailed design

Qualified import syntax will be revised to the following

module-decl -> module <module-path>
import-decl -> <access-level-modifier> import <module-path> <(opt) import-directive-list>
module-path -> <identifier>
           -> <identifier>.<import-path>
import-directive-list -> <import-directive>
                     -> <import-directive> <import-directive-list>
import-directive -> using (<identifier>, ...)
                -> hiding (<identifier>, ...)
                -> renaming (<identifier>, to: <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 3 operations:

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

// The only visible parts of Foundation in this file are
// Date.init(), Date.hashValue, and Date.description.
import Foundation.Date using (Date.init(), Date.hashValue, Date.description)
2) hiding: The hiding directive is followed by a list of identifiers within the imported module that should be hidden from this file.

// Imports all of Foundation.Date except `Date.compare()`
import Foundation.Date hiding (Date.compare())
3) renaming: The renaming directive is followed by a list of identifiers separated by to: that should be exposed to this file but renamed.

// Imports all of Dispatch.DispatchQueue but renames the static member
// DispatchQueue.main, to DispatchQueue.mainQueue
import Dispatch.DispatchQueue renaming (DispatchQueue.Type.main to: DispatchQueue.Type.
mainQueue)

// Renaming can also rename modules. All members of UIKit have to be qualified with
// `UI` now.
import UIKit renaming (UIKit, to: UI)
Import directives chain to one another and can be used to create a fine-grained module import:

// Imports all of Foundation except `DateFormatter` and renames `Cache` to `LRUCache`
import Foundation
hiding (DateFormatter) renaming (Cache to: LRUCache)

// Imports SCNNode except SCNNode.init(mdlObject:) and renames `.description` to
// `.nodeDescription`
import SceneKit
using (SCNNode)
               renaming (SCNNode
.description, to: SCNNode.
nodeDescription)
               hiding (SCNNode
.init(mdlObject:))
Directive chaining occurs left-to-right:

// This says to 1) Hide nothing 2) Use nothing 3) rename Int to INT. It is invalid
// because 1) We will show everything 2) Then hide everything 3) Therefore Int is unavailable, error.
import Swift hiding () using () renaming (Int
, to: INT)

// 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. 3) Double is unavailable, error.
import Swift using (Int) hiding (String) renaming (Double
, to: Triple)

// 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)
Module scope is delimited by the keyword module followed by a fully qualified name and must occur as the first declaration in a file. For example:

// ./Math/Integers/Arithmetic.swift

module Math
.Integers.
Arithmetic

public protocol
_IntegerArithmetic {}

public struct
_Abs {}

@_versioned
internal func _abs<Args>(_ args: Args) ->
(_Abs, Args) {}

// ./Math/Integers.swift

module Math
.
Integers

// _abs is visible in this module and all others within the project,
// but is not exported along with it.
internal import Math.Integers.Arithmetic

public protocol IntegerArithmetic : _IntegerArithmetic, Comparable
{}

public protocol SignedNumber : Comparable
, ExpressibleByIntegerLiteral {}

// Math.swift

module Math

// Exports the entire public contents of Math.Integers, but nothing in
// Math.Integers.Arithmetic.
public import Math.Integers
Modules names are tied to a directory structure that describes their location relative to the current module and it will now be an error to violate this rule. For example:

module String // lives in ./String.swift

module
String.Core // lives in ./String/Core.swift

module
String.Core.Internals.Do.You.Even.Write // lives in ./String/Core/Internals/Do/You/Even/Write.swift
Existing projects that do not adopt these rules will still retain their implicit module name (usually defined as the name of the framework or application that is being built) and may continue to use whatever directory structure they wish, however they may not declare any explicit modules.

This proposal also solves the problem of module export. A module that is imported without an access level modifier will default to an internal import per usual. However, when it is useful to fully expose the public content of submodules to a client, a public modifier can be used. Similarly, when it is useful to access internal or [file]private APIs, but not expose them to clients, those access modifiers may be used. The rule of thumb is: Only identifiers that are at least as visible as the qualifier on the import make for valid import declarations. For example:

// A submodule declaring a `private` class that gets imported with
// an `internal` qualifier with a `using` directive is an invalid import
// declaration.

module Foo
.
Bar

private class
PrivateThing {}

module Foo

// Error: PrivateThing not visible, use `private import`
import Foo.Bar using (PrivateThing)
// However, a submodule declaring a `public` struct that gets imported with
// an `private` qualifier is a valid import declaration.

module Foo
.
Bar

public class
PublicThing {}

module Foo

// All good! Foo can see Foo.Bar.PrivateThing.
private import Foo.Bar using (PublicThing)
Because import directives are file-local, they will never be exported along with a public import and will default to exporting the entire contents of the module as though you had never declared them.

// In this file and this file alone, the directives apply. To the user
// of this module, it is as though this declaration were simply:
// public import Foundation.Date
public import Foundation.Date hiding (Date.init
())
                             renaming (Date
.Type.
distantPast,
                                       to: Date
.Type.
letsGoLivingInThePast,
                                       Date
.Type.
timeIntervalSinceReferenceDate,
                                       to: Date
.Type.
startOfTheUniverse)
                             renaming (Date
.Type.<, to: Date.Type.<<<<<)
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. Code that is not organized into modules will remain unaffected and organized into one contiguous top-level module. However, it is strongly recommended that frameworks be decomposed and reorganized around the new module system.

As a case study, the public interface to the standard library appears to already be mostly broken down into submodules as described in GroupInfo.json.

Code that is defined in modulemaps already defines a module structure that can be imported directly into this scheme.

Alternatives considered

Module export can also be placed on the module declaration itself. The relevant parts of the grammar that have changed are below with an example:

module-decl -> <access-level-modifier> module <module-path>
import-decl -> import <module-path> <(opt) import-directive-list>

private module String.Core.
Internals

// Shh, it's a secret.
While this style makes it immediately obvious to the library author which modules are public or private, it causes the consumer problems because submodule exports are no longer explicit and are entirely ad-hoc. In the interest of enabling, for one, users of IDEs to drill into public submodules, making export local to import seems more appropriate.
_______________________________________________
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