> 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

Reply via email to