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

T.J. Usiyan griotspeak at gmail.com
Tue Jul 19 00:04:44 CDT 2016


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 at 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 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 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 at swift.org
> > https://lists.swift.org/mailman/listinfo/swift-evolution
>
> _______________________________________________
> 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/20160719/1791e99a/attachment.html>


More information about the swift-evolution mailing list