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 
> <[email protected]> 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
> [email protected]
> https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to