> On Jul 18, 2016, at 3:21 PM, John McCall <[email protected]> wrote:
>
>>
>> On Jul 18, 2016, at 2:09 PM, Robert Widmann via swift-evolution
>> <[email protected] <mailto:[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
>> <rdar://17630570>) I sent fairly early on that didn’t receive a response, so
>> I started a swift-evolution
>> <http://permalink.gmane.org/gmane.comp.lang.swift.evolution/1378> thread
>> discussing the basics of this proposal. It has been refined and expanded a
>> bit to include an effort to make Swift modules explicit and updated with the
>> feedback of that first thread. Contents of the proposal are inline and can
>> also be had as a gist
>> <https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6> or on
>> Github. <https://github.com/apple/swift-evolution/pull/440>
>>
>> Cheers,
>>
>> ~Robert Widmann
>>
>> Qualified Imports and Modules
>>
>> Proposal: SE-NNNN
>> <https://gist.github.com/CodaFi/NNNN-first-class-qualified-imports.md>
>> Authors: Robert Widmann <https://github.com/codafi>, Harlan Haskins
>> <https://github.com/harlanhaskins>, TJ Usiyan <https://github.com/griotspeak>
>> Status: Awaiting review
>> Review manager: TBD
>>
>> <https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#introduction>Introduction
>>
>> We propose a complete overhaul of the qualified imports syntax and semantics
>> and the introduction of a module system.
>>
>>
>> <https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#motivation>Motivation
>>
>> Swift code is modular by default. However, it is not clear how to decompose
>> existing modules further into submodules. In addition, it is difficult to
>> tell how importing a module affects its export to consumers of a library.
>> This leads many to either fake namespaces with enums, attempt to structure
>> Swift code with modulemaps, or use a large amount of version-control
>> submodules. All of these can be rolled into one complete package in the form
>> of a comprehensive rethink of the qualified import system and the
>> introduction of a module system.
>>
>>
>> <https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#proposed-solution>Proposed
>> solution
>>
>> Modules will now become an explicit part of working with canonical Swift
>> code. The grammar and semantics of qualified imports will change completely
>> with the addition of import qualifiers and import directives. We also
>> introduce three new contextual keywords: using, hiding, and renaming, to
>> facilitate fine-grained usage of module contents.
>>
>>
>> <https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#detailed-design>Detailed
>> design
>>
>> Qualified import syntax will be revised to the following
>>
>> module-decl -> module <module-path>
>> import-decl -> <access-level-modifier> import <module-path> <(opt)
>> import-directive-list>
>> module-path -> <identifier>
>> -> <identifier>.<import-path>
>> import-directive-list -> <import-directive>
>> -> <import-directive> <import-directive-list>
>> import-directive -> using (<identifier>, ...)
>> -> hiding (<identifier>, ...)
>> -> renaming (<identifier>, to: <identifier>, ...)
>> This introduces the concept of an import directive. An import directive is a
>> file-local modification of an imported identifier. A directive can be one of
>> 3 operations:
>>
>> 1) using: The using directive is followed by a list of identifiers within
>> the imported module that should be exposed to this file.
>>
>> // The only visible parts of Foundation in this file are
>> // Date.init(), Date.hashValue, and Date.description.
>> import Foundation.Date using (Date.init(), Date.hashValue, Date.description)
>> 2) hiding: The hiding directive is followed by a list of identifiers within
>> the imported module that should be hidden from this file.
>>
>> // Imports all of Foundation.Date except `Date.compare()`
>> import Foundation.Date hiding (Date.compare())
> It's unfortunate that this proposal requires the identifiers to be
> re-qualified when the imported module is actually the name of a type.
I considered making that a feature of this proposal but I had a tough time
reconciling removing the decl specifier from qualified imports and having an
unambiguous notation for Swift declarations.
> It seems to me that whether e.g. a type is defined in its own sub-module is
> a detail that users won't really appreciate and which probably shouldn't be
> surfaced to them. In fact, in general I'm concerned about this turning the
> file and directory organization of a project into API.
It’s a detail that they’ve already had to have surfaced if they use the
existing syntax.
> In fact, in general I'm concerned about this turning the file and directory
> organization of a project into API.
It’s a legitimate concern and one that I share considering there is a way to
abuse this restriction (*cough* Java), or try to rally around a limited set of
namespaces for common controls.
>
>> This proposal also solves the problem of module export. A module that is
>> imported without an access level modifier will default to an internal import
>> per usual. However, when it is useful to fully expose the public content of
>> submodules to a client, a public modifier can be used. Similarly, when it is
>> useful to access internal or [file]private APIs, but not expose them to
>> clients, those access modifiers may be used.
>>
> These uses of access modifiers feel inconsistent to me. "public import Foo"
> exports the contents of Foo as if they were members of my module, but
> "private import Foo" imports the private APIs (?) of Foo? That is not the
> same interpretive rule.
>
It’s not, and it’s that way intentionally. Non-public imports do not
re-export. The wording around this is shabby, I’ll work to improve it.
> I think the more consistent analogy for "private import" would be to say that
> the public members of Foo are visible in this file only (i.e. the default
> behavior of "import" today, which I think we would want to keep), whereas you
> could do an "internal import" to make the members of Foo visible throughout
> the current module (potentially useful if you have an interesting set of
> common modifications you want to make).
>
> I don't know why you think it should be possible to import the private
> declarations of a module. That seems completely contrary to the
> access-control design. I agree that it's useful to have sub-modules expose
> APIs that are only usable by other parts of the larger module, but I think
> the Swiftier design would be for that to be opt-in on the declaration
> somehow, or at least to specify how it interacts with "internal".
>
I was approached by users at WWDC that did wish to have some way of grouping a
bunch of private Swift files that should “know about each other’s internal
stuff”. At the time this was the semantics that seemed to match that and
stayed in-line with what a `private import` could possibly do. Perhaps this
kind of import can be banned-by-diagnostic in that case.
> Also, it is completely unclear to me why modifiers like "renaming" don't
> change how the imported module's declarations are re-exported.
>
Because importing a library shouldn’t be able to change whatever it likes and
break client code on re-export. I don’t have a particularly compelling
use-case for allowing user-specified mappings to escape file scope and neither
do many other languages I can find that permit this feature. If you have one
I’d like to know about it.
> John.
>
>> The rule of thumb is: Only identifiers that are at least as visible as the
>> qualifier on the import make for valid import declarations. For example:
>>
>> // A submodule declaring a `private` class that gets imported with
>> // an `internal` qualifier with a `using` directive is an invalid import
>> // declaration.
>> module Foo.Bar
>>
>> private class PrivateThing {}
>>
>> module Foo
>>
>> // Error: PrivateThing not visible, use `private import`
>> import Foo.Bar using (PrivateThing)
>> // However, a submodule declaring a `public` struct that gets imported with
>> // an `private` qualifier is a valid import declaration.
>> module Foo.Bar
>>
>> public class PublicThing {}
>>
>> module Foo
>>
>> // All good! Foo can see Foo.Bar.PrivateThing.
>> private import Foo.Bar using (PublicThing)
>> Because import directives are file-local, they will never be exported along
>> with a public import and will default to exporting the entire contents of
>> the module as though you had never declared them.
>>
>> // In this file and this file alone, the directives apply. To the user
>> // of this module, it is as though this declaration were simply:
>> // public import Foundation.Date
>> public import Foundation.Date hiding (Date.init())
>> renaming (Date.Type.distantPast,
>> to: Date.Type.letsGoLivingInThePast,
>>
>> Date.Type.timeIntervalSinceReferenceDate,
>> to: Date.Type.startOfTheUniverse)
>> renaming (Date.Type.<, to: Date.Type.<<<<<)
>>
>> <https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#impact-on-existing-code>Impact
>> on existing code
>>
>> Existing code that is using qualified module import syntax (import
>> {func|class|typealias|class|struct|enum|protocol} <qualified-name>) will be
>> deprecated. Code that is not organized into modules will remain unaffected
>> and organized into one contiguous top-level module. However, it is strongly
>> recommended that frameworks be decomposed and reorganized around the new
>> module system.
>>
>> As a case study, the public interface to the standard library appears to
>> already be mostly broken down into submodules as described in GroupInfo.json
>> <https://github.com/apple/swift/blob/master/stdlib/public/core/GroupInfo.json>.
>>
>> Code that is defined in modulemaps already defines a module structure that
>> can be imported directly into this scheme.
>>
>>
>> <https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6#alternatives-considered>Alternatives
>> considered
>>
>> Module export can also be placed on the module declaration itself. The
>> relevant parts of the grammar that have changed are below with an example:
>>
>> module-decl -> <access-level-modifier> module <module-path>
>> import-decl -> import <module-path> <(opt) import-directive-list>
>> private module String.Core.Internals
>>
>> // Shh, it's a secret.
>> While this style makes it immediately obvious to the library author which
>> modules are public or private, it causes the consumer problems because
>> submodule exports are no longer explicit and are entirely ad-hoc. In the
>> interest of enabling, for one, users of IDEs to drill into public
>> submodules, making export local to import seems more appropriate.
>> _______________________________________________
>> swift-evolution mailing list
>> [email protected] <mailto:[email protected]>
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution