[Proposal] Qualified Imports and Modules

Haskell has much more than import everything and import itemized.
https://wiki.haskell.org/Import

Hiding is important for avoiding collisions of name and/or function.

···

On Mon, Jul 18, 2016 at 9:19 PM, Joe Groff via swift-evolution < swift-evolution@swift.org> wrote:

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

Totally agree with Joe here. I’d like to see a simplified version of the import design through ASAP. Mot people will quickly agree with it. The rest of the proposal is much more up for discussion IMHO.

···

On 19 Jul 2016, at 03:19, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

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

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&gt; 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&gt; or on Github. <https://github.com/apple/swift-evolution/pull/440&gt;

Cheers,

~Robert Widmann

Qualified Imports and Modules

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

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

<qualified-imports.md · GitHub

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.

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

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

Yes, I can see that there are difficulties here. I'm just worried that an abruptly-designed solution is going to carve those difficulties into stone.

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.

Hmm? We don't have sub-modules today. "import Foundation.NSObject" just brings that specific declaration in; there's no semantic differentiation between that and importing a module.

Relatedly, your proposal side-steps any discussion about what happens if you try to name a sub-module the same as a type. You must have done that very carefully, because one of your examples clearly envisages String being in its own sub-module. :)

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.

Yeah, I don't think this is good. I think my suggestion makes more sense, where a non-private import exports to whatever the named scope is. This draws the current behavior consistently into your model (the default import rule is private), and the "internal import" concept actually seems like a pretty useful feature in a world where you anticipate people doing significant renames on import.

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.

This sounds more like it's calling for better definition of the access interactions between sub-modules.

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.

Sharing user-specified mappings between files seems better than forcing them to be copy-and-pasted. You could lock down on public exports that rename mappings if you're worried about that.

John.

···

On Jul 18, 2016, at 3:53 PM, Robert Widmann <rwidmann@apple.com> wrote:

On Jul 18, 2016, at 3:21 PM, John McCall <rjmccall@apple.com <mailto: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:

They wouldn't have access to each others' internal scopes by default, would they?

In general, I get the sense that this proposal is hostile to the current idea that a module (or submodule) consists of multiple files, each of which has both intra-file privacy and inter-file sharing with other parts of the module. I think this is a natural and extremely useful way to design software. Moreover, I think throwing this concept out goes explicitly against the grain of current Swift access control mechanisms. In a world where every file is its own module, what's the difference between `internal` and `fileprivate`? Are we going to end up in an Objective-C-style situation of giant import lists at the top of every file, listing stuff that's in the same module? Doesn't that go against the goal of reducing boilerplate?

I would prefer to see:

1. Each file is allowed one `submodule` declaration as the first non-comment line of code in the file. It does not include the main module name, only the submodule name (so `UIKit.UIGestureRecognizerSubclass` would simply have `submodule UIGestureRecognizerSubclass` at the top). If there is none, the file is part of the main module. (It *might* make sense to have both public submodules, which anyone can import, and internal submodules, which can only be imported within the top-level module, but I'll need to think about that.)

2. Filenames are freeform; any file can declare itself to belong to any submodule. Obviously, best practice would be to give your files sensible names that have some kind of link to the submodule name, but this would be a linter concern.

3. Each submodule has its own `internal` scope. Submodules can import the `internal` scopes of specific peer modules with an annotation like `@testable` (but probably renamed). Tests are treated as a submodule of the main module, so they can participate in this mechanism just like everyone else.

4. `import` only ever imports the `public` (or `internal`, with the `@testable` equivalent) symbols in the specified submodule. It re-exposes them with the access modifier on the `import` statement, or `private` by default. It does not re-expose `internal` symbols as `public`. `using`, `hiding`, and `renaming` apply to all comers, not just the current file.

I think this approach would harmonize much better with current Swift features and code organization practices, while offering several new features (umbrella modules, exposing certain symbols only when a submodule is explicitly imported, multiple `internal` scopes within a top-level module) which would be very useful.

···

On Jul 18, 2016, at 4:50 PM, T.J. Usiyan <griotspeak@gmail.com> wrote:

* 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.

--
Brent Royal-Gordon
Architechies

* 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.

They wouldn't have access to each others' internal scopes by default, would they?

In general, I get the sense that this proposal is hostile to the current idea that a module (or submodule) consists of multiple files, each of which has both intra-file privacy and inter-file sharing with other parts of the module. I think this is a natural and extremely useful way to design software. Moreover, I think throwing this concept out goes explicitly against the grain of current Swift access control mechanisms. In a world where every file is its own module, what's the difference between `internal` and `fileprivate`? Are we going to end up in an Objective-C-style situation of giant import lists at the top of every file, listing stuff that's in the same module? Doesn't that go against the goal of reducing boilerplate?

I would prefer to see:

1. Each file is allowed one `submodule` declaration as the first non-comment line of code in the file. It does not include the main module name, only the submodule name (so `UIKit.UIGestureRecognizerSubclass` would simply have `submodule UIGestureRecognizerSubclass` at the top). If there is none, the file is part of the main module. (It *might* make sense to have both public submodules, which anyone can import, and internal submodules, which can only be imported within the top-level module, but I'll need to think about that.)

That is one of the alternatives considered. It may be possible through annotation of the module itself to express this kind of thing.

2. Filenames are freeform; any file can declare itself to belong to any submodule. Obviously, best practice would be to give your files sensible names that have some kind of link to the submodule name, but this would be a linter concern.

How does this interact with duplicate declarations?

3. Each submodule has its own `internal` scope. Submodules can import the `internal` scopes of specific peer modules with an annotation like `@testable` (but probably renamed).

"Peer modules” is something we can lock down without having to introduce even more scopes and fits well within this proposal. A restriction like “a module may only import private members from submodules 1-level deeper than themselves” for example.

Tests are treated as a submodule of the main module, so they can participate in this mechanism just like everyone else.

Or we could keep the existing @testable import syntax. It will still work exactly the way it always has under this proposal.

4. `import` only ever imports the `public` (or `internal`, with the `@testable` equivalent) symbols in the specified submodule. It re-exposes them with the access modifier on the `import` statement, or `private` by default. It does not re-expose `internal` symbols as `public`. `using`, `hiding`, and `renaming` apply to all comers, not just the current file.

We do not allow you to re-export any API that is not public. The wording around the section you keep bringing up is vague and needs to be fixed.

···

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

On Jul 18, 2016, at 4:50 PM, T.J. Usiyan <griotspeak@gmail.com> wrote:

I think this approach would harmonize much better with current Swift features and code organization practices, while offering several new features (umbrella modules, exposing certain symbols only when a submodule is explicitly imported, multiple `internal` scopes within a top-level module) which would be very useful.

--
Brent Royal-Gordon
Architechies

2. Filenames are freeform; any file can declare itself to belong to any submodule. Obviously, best practice would be to give your files sensible names that have some kind of link to the submodule name, but this would be a linter concern.

How does this interact with duplicate declarations?

Then they are part of the same submodule.

For instance, take a look at the GroupInfo.json file cited in the proposal: <https://github.com/apple/swift/blob/master/stdlib/public/core/GroupInfo.json&gt; The Swift.String submodule consists of 18 separate files. Each of these would have `submodule String` at the top, and they would all share a single `internal` scope separate from the internal scope of the Swift module as a whole.

3. Each submodule has its own `internal` scope. Submodules can import the `internal` scopes of specific peer modules with an annotation like `@testable` (but probably renamed).

"Peer modules” is something we can lock down without having to introduce even more scopes and fits well within this proposal. A restriction like “a module may only import private members from submodules 1-level deeper than themselves” for example.

I assume you mean `internal` members; exposing `private` members outside the scope they're declared in, or `fileprivate` members outside the file they're declared in, is problematic.

What I'm suggesting is that, for instance, StringCharacterView.swift can write:

  submodule String
  @testable /* or whatever */ import Swift.Collection
  
  extension String {
    struct CharacterView: Collection {
      // And we can use internal-only Collection calls here
    }
  }

We could certainly make rules restricting you to only importing (say) immediate siblings or immediate children, but I think that might end up being overly bureaucratic. The combination of a module and its submodules form a single whole, released together and controlled by the same organization. I see little need for elaborate tying of hands.

Tests are treated as a submodule of the main module, so they can participate in this mechanism just like everyone else.

Or we could keep the existing @testable import syntax. It will still work exactly the way it always has under this proposal.

I'm trying to merge two similar mechanisms into one. We need some way to have SPIs between submodules within a module; it seems sensible to rework `@testable`, which creates SPIs between a module and its tests, into this mechanism.

4. `import` only ever imports the `public` (or `internal`, with the `@testable` equivalent) symbols in the specified submodule. It re-exposes them with the access modifier on the `import` statement, or `private` by default. It does not re-expose `internal` symbols as `public`. `using`, `hiding`, and `renaming` apply to all comers, not just the current file.

We do not allow you to re-export any API that is not public. The wording around the section you keep bringing up is vague and needs to be fixed.

Yes—I'm merely restating that to emphasize that it's part of my approach.

···

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

--
Brent Royal-Gordon
Architechies

2. Filenames are freeform; any file can declare itself to belong to any submodule. Obviously, best practice would be to give your files sensible names that have some kind of link to the submodule name, but this would be a linter concern.

How does this interact with duplicate declarations?

Then they are part of the same submodule.

For instance, take a look at the GroupInfo.json file cited in the proposal: <https://github.com/apple/swift/blob/master/stdlib/public/core/GroupInfo.json&gt; The Swift.String submodule consists of 18 separate files. Each of these would have `submodule String` at the top, and they would all share a single `internal` scope separate from the internal scope of the Swift module as a whole.

It seems like that kind of scoping can just fall under “whatever my submodules are, I’d like to be able to see into them”. If you think of a module and its submodules as a tree, an internal module scope is a branch and its children and a set of private declarations are the leaves.

3. Each submodule has its own `internal` scope. Submodules can import the `internal` scopes of specific peer modules with an annotation like `@testable` (but probably renamed).

"Peer modules” is something we can lock down without having to introduce even more scopes and fits well within this proposal. A restriction like “a module may only import private members from submodules 1-level deeper than themselves” for example.

I assume you mean `internal` members; exposing `private` members outside the scope they're declared in, or `fileprivate` members outside the file they're declared in, is problematic.

What I'm suggesting is that, for instance, StringCharacterView.swift can write:

  submodule String
  @testable /* or whatever */ import Swift.Collection
  
  extension String {
    struct CharacterView: Collection {
      // And we can use internal-only Collection calls here
    }
  }

We could certainly make rules restricting you to only importing (say) immediate siblings or immediate children, but I think that might end up being overly bureaucratic. The combination of a module and its submodules form a single whole, released together and controlled by the same organization. I see little need for elaborate tying of hands.

Which is why the proposal originally allowed you to say something like internal import Swift.Collection rather than express a group of “friends” that can import from each other. If you want to ask for the contents of a non-exported module from another within the same project, at least you have to be explicit about what kind of access you want. I think we have similar goals for private access here, it’s just a matter of expression.

···

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

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

Tests are treated as a submodule of the main module, so they can participate in this mechanism just like everyone else.

Or we could keep the existing @testable import syntax. It will still work exactly the way it always has under this proposal.

I'm trying to merge two similar mechanisms into one. We need some way to have SPIs between submodules within a module; it seems sensible to rework `@testable`, which creates SPIs between a module and its tests, into this mechanism.

4. `import` only ever imports the `public` (or `internal`, with the `@testable` equivalent) symbols in the specified submodule. It re-exposes them with the access modifier on the `import` statement, or `private` by default. It does not re-expose `internal` symbols as `public`. `using`, `hiding`, and `renaming` apply to all comers, not just the current file.

We do not allow you to re-export any API that is not public. The wording around the section you keep bringing up is vague and needs to be fixed.

Yes—I'm merely restating that to emphasize that it's part of my approach.

--
Brent Royal-Gordon
Architechies

What about using duplicate declarations to indicate that two files are part
of the same module?

···

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

> On Jul 18, 2016, at 5:26 PM, Brent Royal-Gordon <brent@architechies.com> > wrote:
>
>> On Jul 18, 2016, at 4:50 PM, T.J. Usiyan <griotspeak@gmail.com> wrote:
>>
>> * 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.
>
> They wouldn't have access to each others' internal scopes by default,
would they?
>
> In general, I get the sense that this proposal is hostile to the current
idea that a module (or submodule) consists of multiple files, each of which
has both intra-file privacy and inter-file sharing with other parts of the
module. I think this is a natural and extremely useful way to design
software. Moreover, I think throwing this concept out goes explicitly
against the grain of current Swift access control mechanisms. In a world
where every file is its own module, what's the difference between
`internal` and `fileprivate`? Are we going to end up in an
Objective-C-style situation of giant import lists at the top of every file,
listing stuff that's in the same module? Doesn't that go against the goal
of reducing boilerplate?
>
> I would prefer to see:
>
> 1. Each file is allowed one `submodule` declaration as the first
non-comment line of code in the file. It does not include the main module
name, only the submodule name (so `UIKit.UIGestureRecognizerSubclass` would
simply have `submodule UIGestureRecognizerSubclass` at the top). If there
is none, the file is part of the main module. (It *might* make sense to
have both public submodules, which anyone can import, and internal
submodules, which can only be imported within the top-level module, but
I'll need to think about that.)

That is one of the alternatives considered. It may be possible through
annotation of the module itself to express this kind of thing.

> 2. Filenames are freeform; any file can declare itself to belong to any
submodule. Obviously, best practice would be to give your files sensible
names that have some kind of link to the submodule name, but this would be
a linter concern.

How does this interact with duplicate declarations?

>
> 3. Each submodule has its own `internal` scope. Submodules can import
the `internal` scopes of specific peer modules with an annotation like
`@testable` (but probably renamed).

"Peer modules” is something we can lock down without having to introduce
even more scopes and fits well within this proposal. A restriction like “a
module may only import private members from submodules 1-level deeper than
themselves” for example.

> Tests are treated as a submodule of the main module, so they can
participate in this mechanism just like everyone else.

Or we could keep the existing @testable import syntax. It will still work
exactly the way it always has under this proposal.

> 4. `import` only ever imports the `public` (or `internal`, with the
`@testable` equivalent) symbols in the specified submodule. It re-exposes
them with the access modifier on the `import` statement, or `private` by
default. It does not re-expose `internal` symbols as `public`. `using`,
`hiding`, and `renaming` apply to all comers, not just the current file.

We do not allow you to re-export any API that is not public. The wording
around the section you keep bringing up is vague and needs to be fixed.

>
> I think this approach would harmonize much better with current Swift
features and code organization practices, while offering several new
features (umbrella modules, exposing certain symbols only when a submodule
is explicitly imported, multiple `internal` scopes within a top-level
module) which would be very useful.
>
> --
> Brent Royal-Gordon
> Architechies
>

There is no effective difference between renaming to a dummy name and hiding. In fact, renaming (Int, to: _) could do that.

Félix

···

Le 18 juil. 2016 à 22:04:44, T.J. Usiyan via swift-evolution <swift-evolution@swift.org> a écrit :

Haskell has much more than import everything and import itemized. Import - HaskellWiki

Hiding is important for avoiding collisions of name and/or function.

On Mon, Jul 18, 2016 at 9:19 PM, Joe Groff via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
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 <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 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 <mailto:swift-evolution@swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution

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

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

Import Foo.* as bar. // has public methodX() and type TypeA
Import Foo2.* as baz. // also has public methodX() and type TypeA

would be interesting because it entirely avoids collisions when using both together. It also provides some documentation at the point of use, as well as allow the IDE to keep its indexing of Foo completely distinct fromthe indexing of the current module. The last point is that it also leads to less cluttered code completion proposal lists

Y = bar.methodX() + baz.methodX()
var v1 = new bar.TypeA()
var v2 = new baz.TypeA()

Regards
(From mobile)

···

On Jul 19, 2016, at 6:50 AM, Félix Cloutier via swift-evolution <swift-evolution@swift.org> wrote:

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

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

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&gt; 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&gt; or on Github. <https://github.com/apple/swift-evolution/pull/440&gt;

Cheers,

~Robert Widmann

Qualified Imports and Modules

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

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

<qualified-imports.md · GitHub

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.

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

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

Yes, I can see that there are difficulties here. I'm just worried that an abruptly-designed solution is going to carve those difficulties into stone.

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.

Hmm? We don't have sub-modules today. "import Foundation.NSObject" just brings that specific declaration in; there's no semantic differentiation between that and importing a module.

We do!

import class Foundation.NSObject

^That’s the syntax we’re trying to replace.

Relatedly, your proposal side-steps any discussion about what happens if you try to name a sub-module the same as a type. You must have done that very carefully, because one of your examples clearly envisages String being in its own sub-module. :)

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.

Yeah, I don't think this is good. I think my suggestion makes more sense, where a non-private import exports to whatever the named scope is. This draws the current behavior consistently into your model (the default import rule is private), and the "internal import" concept actually seems like a pretty useful feature in a world where you anticipate people doing significant renames on import.

I honestly don’t anticipate this being as much of a thing. There should be minor cosmetic updates to local functions and possibly more descriptive renamings of common functions, but if you want to export a renaming today you can always find a way to alias existing definitions to other definitions (nominal types with typealiases, functions with top-level let-bound closures, members are tricky but that’s what extensions are for) rather than go through lookup. Regardless, that is a more consistent 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".

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.

This sounds more like it's calling for better definition of the access interactions between sub-modules.

Breaking true private scope on decls is something we’re already doing with the fileprivate updates. If we wanted to draw inspiration from that we could introduce a rule where a private import is only allowed to reference a submodule “one-level-deeper” than itself (to match the “private decl’s members can be seen effectively 'one scope level up’ rule).

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.

Sharing user-specified mappings between files seems better than forcing them to be copy-and-pasted. You could lock down on public exports that rename mappings if you're worried about that.

Now it seems like we’re defining a macro system. Renamings of identifiers are probably going to be the least common of the three and it’s not clear what happens if a user internally is using multiple renamings for the same identifier if they’re exported around the project. Plus, because declarations would be decentralized, it’s entirely possible that a multi-person project will wind up with people trampling over each other’s renamings than a consistent organized mapping. It seems like we want to encourage the definition of new symbols and aliases for significant API changes and renamings for local fixes and nitpicks where it wouldn’t otherwise be possible to use an API because of a conflict.

···

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

On Jul 18, 2016, at 3:53 PM, Robert Widmann <rwidmann@apple.com <mailto:rwidmann@apple.com>> wrote:

On Jul 18, 2016, at 3:21 PM, John McCall <rjmccall@apple.com <mailto: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.