[swift-evolution] [Proposal] Qualified Imports and Modules

Xiaodi Wu xiaodi.wu at gmail.com
Mon Jul 18 20:16:42 CDT 2016


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

> Hello all,
>
> TJ Usiyan, Harlan Haskins, and I have been working on a proposal to rework
> qualified imports and introduce an explicit module system to Swift that
> we’d like to publish for your viewing pleasure.
>
> The initial impetus was set out in a radar (rdar://17630570) I sent
> fairly early on that didn’t receive a response, so I started a
> swift-evolution
> <http://permalink.gmane.org/gmane.comp.lang.swift.evolution/1378> thread
> discussing the basics of this proposal.  It has been refined and expanded a
> bit to include an effort to make Swift modules explicit and updated with
> the feedback of that first thread.  Contents of the proposal are inline and can
> also be had as a gist
> <https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6> or on
> Github. <https://github.com/apple/swift-evolution/pull/440>
>
> Cheers,
>
> ~Robert Widmann
>
> Qualified Imports and Modules
>
>    - Proposal: SE-NNNN
>    <https://gist.github.com/CodaFi/NNNN-first-class-qualified-imports.md>
>    - Authors: Robert Widmann <https://github.com/codafi>, Harlan Haskins
>    <https://github.com/harlanhaskins>, TJ Usiyan
>    <https://github.com/griotspeak>
>    - Status: Awaiting review
>    - Review manager: TBD
>
>
> <https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#introduction>
> Introduction
>
> We propose a complete overhaul of the qualified imports syntax and
> semantics and the introduction of a module system.
>
> <https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#motivation>
> Motivation
>
> Swift code is modular by default. However, it is not clear how to
> decompose existing modules further into submodules. In addition, it is
> difficult to tell how importing a module affects its export to consumers of
> a library. This leads many to either fake namespaces with enums, attempt to
> structure Swift code with modulemaps, or use a large amount of
> version-control submodules. All of these can be rolled into one complete
> package in the form of a comprehensive rethink of the qualified import
> system and the introduction of a module system.
>
> <https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#proposed-solution>Proposed
> solution
>
> Modules will now become an explicit part of working with canonical Swift
> code. The grammar and semantics of qualified imports will change completely
> with the addition of *import qualifiers* and *import directives*. We also
> introduce three new contextual keywords: using, hiding, and renaming, to
> facilitate fine-grained usage of module contents.
>
> <https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#detailed-design>Detailed
> design
>
> Qualified import syntax will be revised to the following
>
> module-decl -> module <module-path>
> import-decl -> <access-level-modifier> import <module-path> <(opt) import-directive-list>
> module-path -> <identifier>
>             -> <identifier>.<import-path>
> import-directive-list -> <import-directive>
>                       -> <import-directive> <import-directive-list>
> import-directive -> using (<identifier>, ...)
>                  -> hiding (<identifier>, ...)
>                  -> renaming (<identifier>, to: <identifier>, ...)
>
> This introduces the concept of an import *directive*. An import directive
> is a file-local modification of an imported identifier. A directive can be
> one of 3 operations:
>
> 1) *using*: The *using* directive is followed by a list of identifiers
> within the imported module that should be exposed to this file.
>
> // The only visible parts of Foundation in this file are // Date.init(), Date.hashValue, and Date.description.import Foundation.Date using (Date.init(), Date.hashValue, Date.description)
>
> 2) *hiding*: The hiding directive is followed by a list of identifiers
> within the imported module that should be hidden from this file.
>
> // Imports all of Foundation.Date except `Date.compare()`import Foundation.Date hiding (Date.compare())
>
> 3) *renaming*: The renaming directive is followed by a list of
> identifiers separated by to: that should be exposed to this file but
> renamed.
>
> // Imports all of Dispatch.DispatchQueue but renames the static member // DispatchQueue.main, to DispatchQueue.mainQueueimport Dispatch.DispatchQueue renaming (DispatchQueue.Type.main to: DispatchQueue.Type.mainQueue)// Renaming can also rename modules.  All members of UIKit have to be qualified with// `UI` now.import UIKit renaming (UIKit, to: UI)
>
> Import directives chain to one another and can be used to create a
> fine-grained module import:
>
> // Imports all of Foundation except `DateFormatter` and renames `Cache` to `LRUCache`import Foundation hiding (DateFormatter) renaming (Cache to: LRUCache)// Imports SCNNode except SCNNode.init(mdlObject:) and renames `.description` to// `.nodeDescription` import SceneKit using (SCNNode)
>                 renaming (SCNNode.description, to: SCNNode.nodeDescription)
>                 hiding (SCNNode.init(mdlObject:))
>
> Directive chaining occurs left-to-right:
>
> // This says to 1) Hide nothing 2) Use nothing 3) rename Int to INT.  It is invalid// because 1) We will show everything 2) Then hide everything 3) Therefore Int is unavailable, error.import Swift hiding () using () renaming (Int, to: INT)// This says to 1) Use Int 2) Hide String 3) rename Double to Triple.  It is invalid// because 1) Int is available 2) String is not, error. 3) Double is unavailable, error.import Swift using (Int) hiding (String) renaming (Double, to: Triple)// Valid.  This will be merged as `using (Int)`import Swift using () using (Int)// Valid.  This will be merged as `hiding (String, Double)`import Swift hiding (String) hiding (Double) hiding ()// Valid (if redundant). This will be merged as `using ()`import Swift using (String) hiding (String)
>
>
I don't mean to detract from the conversation on the deeper design aspects
of this proposal, but in terms of the proposed syntax I think there's
opportunity for simplification. I understand the desire to use parentheses,
especially to avoid taking up `to` even as a contextual keyword, but I
think it is possible to improve clarity by way of some tweaks:

```
import Swift using Int

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

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


> Module scope is delimited by the keyword module followed by a fully
> qualified name and must occur as the first declaration in a file. For
> example:
>
> // ./Math/Integers/Arithmetic.swift
> module Math.Integers.Arithmetic
> public protocol _IntegerArithmetic {}
> public struct _Abs {}
> @_versionedinternal func _abs<Args>(_ args: Args) -> (_Abs, Args) {}
> // ./Math/Integers.swift
> module Math.Integers
> // _abs is visible in this module and all others within the project, // but is not exported along with it.internal import Math.Integers.Arithmetic
> public protocol IntegerArithmetic : _IntegerArithmetic, Comparable {}public protocol SignedNumber : Comparable, ExpressibleByIntegerLiteral {}
>
> // Math.swift
> module Math
> // Exports the entire public contents of Math.Integers, but nothing in // Math.Integers.Arithmetic.public import Math.Integers
>
> Modules names are tied to a directory structure that describes their
> location relative to the current module and it will now be an error to
> violate this rule. For example:
>
> module String // lives in ./String.swift
> module String.Core // lives in ./String/Core.swift
> module String.Core.Internals.Do.You.Even.Write // lives in ./String/Core/Internals/Do/You/Even/Write.swift
>
> Existing projects that do not adopt these rules will still retain their *implicit
> module name* (usually defined as the name of the framework or application
> that is being built) and may continue to use whatever directory structure
> they wish, however they may not declare any explicit modules.
>
> This proposal also solves the problem of module *export*. A module that
> is imported without an access level modifier will default to an internal import
> per usual. However, when it is useful to fully expose the public content of
> submodules to a client, a public modifier can be used. Similarly, when it
> is useful to access internal or [file]private APIs, but not expose them
> to clients, those access modifiers may be used. The rule of thumb is: Only
> identifiers that are at least as visible as the qualifier on the import
> make for valid import declarations. For example:
>
> // A submodule declaring a `private` class that gets imported with // an `internal` qualifier with a `using` directive is an invalid import // declaration.
> module Foo.Bar
> private class PrivateThing {}
>
> module Foo
> // Error: PrivateThing not visible, use `private import`import Foo.Bar using (PrivateThing)
>
> // However, a submodule declaring a `public` struct that gets imported with // an `private` qualifier is a valid import declaration.
> module Foo.Bar
> public class PublicThing {}
>
> module Foo
> // All good!  Foo can see Foo.Bar.PrivateThing.private import Foo.Bar using (PublicThing)
>
> Because import directives are file-local, they will never be exported
> along with a public import and will default to exporting the entire
> contents of the module as though you had never declared them.
>
> // In this file and this file alone, the directives apply.  To the user// of this module, it is as though this declaration were simply:// public import Foundation.Datepublic import Foundation.Date hiding (Date.init())
>                               renaming (Date.Type.distantPast,
>                                         to: Date.Type.letsGoLivingInThePast,
>                                         Date.Type.timeIntervalSinceReferenceDate,
>                                         to: Date.Type.startOfTheUniverse)
>                               renaming (Date.Type.<, to: Date.Type.<<<<<)
>
>
> <https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#impact-on-existing-code>Impact
> on existing code
>
> Existing code that is using qualified module import syntax (import
> {func|class|typealias|class|struct|enum|protocol} <qualified-name>) will
> be deprecated. Code that is not organized into modules will remain
> unaffected and organized into one contiguous top-level module. However, it
> is strongly recommended that frameworks be decomposed and reorganized
> around the new module system.
>
> As a case study, the public interface to the standard library appears to
> already be mostly broken down into submodules as described in
> GroupInfo.json
> <https://github.com/apple/swift/blob/master/stdlib/public/core/GroupInfo.json>
> .
>
> Code that is defined in modulemaps already defines a module structure that
> can be imported directly into this scheme.
>
> <https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#alternatives-considered>Alternatives
> considered
>
> Module export can also be placed on the module declaration itself. The
> relevant parts of the grammar that have changed are below with an example:
>
> module-decl -> <access-level-modifier> module <module-path>
> import-decl -> import <module-path> <(opt) import-directive-list>
>
> private module String.Core.Internals
> // Shh, it's a secret.
>
> While this style makes it immediately obvious to the library author which
> modules are public or private, it causes the consumer problems because
> submodule exports are no longer explicit and are entirely ad-hoc. In the
> interest of enabling, for one, users of IDEs to drill into public
> submodules, making export local to import seems more appropriate.
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160718/120e3a8c/attachment-0001.html>


More information about the swift-evolution mailing list