> On Jul 18, 2016, at 4:34 PM, Brent Royal-Gordon <[email protected]> 
> wrote:
> 
>> 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.
> 
> This is really interesting. A few incomplete comments:
> 
>> 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())
> [snip]
>> // Exports the entire public contents of Math.Integers, but nothing in 
>> // Math.Integers.Arithmetic.
>> public import Math.Integers
> 
> Would this work?
> 
>       module UIKit
>       
>       public import UIKit.UIGestureRecognizerSubclass.UIGestureRecognizer
>               hiding (UIGestureRecognizer.touchesBegan(_:with:), 
> UIGestureRecognizer.touchesMoved(_:with:), …)
> 
> (If so, we'd need a way to hide only the setter of a property, not the 
> getter.)

Yes, that would work.  Notation for hiding setters and getters through the 
import system (probably best to do it internally, but I see what you mean) can 
ideally be another dot qualifier

public import Foo hiding (Foo.bar) // Hide the whole thing
public import Foo hiding (Foo.bar.set) // Hide the setter.
public import Foo hiding (Foo.bar.get) // Hide the getter [Not sure why you’d 
want this].

>> 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
> 
> I think this is a mistake for several reasons:
> 
> * You may need to split a single submodule across multiple files, but this 
> rule doesn't allow that.
> 
> * The module declaration and filename contain redundant information, and one 
> of them could get out of sync with the other.
> 
> * Xcode doesn't like to organize things into folders on disk and will fight 
> you tooth and nail.
> 
> * Deeply nested folders are a usability issue. Never forget the jury in 
> Oracle v. Google: 
> https://www.geek.com/wp-content/uploads/2016/05/courtroomhijinks.png
> 
> At the very least, I would like to see allowances for multi-file 
> submodules—String/Core/Internals/Do/You/Even/Write**.swift. Better would be 
> to use long filenames—String.Core.Internals.Do.You.Even.Write*.swift. Even 
> better would be to just allow freeform naming and trust programmers to 
> organize their projects sanely.

We are not proposing a Java-style module system so much as an extension of the 
existing one to submodules the way they are used today for frameworks like 
Darwin.  Projects no longer require reverse-DNS-style directory structures and 
nesting of submodules to great depth can grow unwieldy, but that may be a sign 
that a project is growing too decentralized.  Large frameworks will decompose 
themselves into submodules in roughly the same way that projects written in 
Objective-C, C, C++ have always decomposed themselves.  The directory structure 
for that style of framework rarely grows to the extend you’re concerned with 
here.  


>> 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.
> 
> It appears the semantics here are "grab all declarations at level X and 
> above, and expose them in this module as level X". This is bizarre for a 
> number of reasons:
> 
> * Currently, `import` can only access `internal` declarations using 
> `@testable`. This would change that.
> 

It would not.  Re-export can only re-export public APIs.

> * Currently, `import` can't access `private` and `fileprivate` declarations 
> at all, and it's not clear what it would mean to add that. What happens if 
> two different parts of the module have different `private` members with the 
> same name? Which do you get?

If we decide to allow this you will get an ambiguity which you can resolve with 
a renaming.  John has made me reconsider the semantics of a private import.

> * Currently, `import` only affects the current file—it's effectively "import 
> `public` members as `fileprivate`". If your default is `internal import`, 
> that would imply that an import statement in one file would, by default, 
> expose the APIs it imported to all files. That's an important change in 
> behavior.
> 

It changes existing behavior in the sense that internal imports are now 
module-scoped rather than project-scoped but you still cannot re-export 
non-public APIs.  I specifically want to *remove* the behavior where an 
imported API winds up recursively importing submodules until your qualified 
import is just garbage anyway.  Your submodules will not see an `internal 
import` in the same way that a project can currently see an internal import 
unless they themselves declare an `internal import` of that module.  And if 
they’re re-exporting public APIs then you probably wanted to see that when you 
imported them anyway.

> I think you're mixing two things together that ought not to be. `import` 
> should always import only public APIs (unless you use `@testable`—which might 
> need a bit of renaming to support the additional use case of SPIs between 
> submodules and supermodules—in which case you also get `internal` APIs).

And it does.

> An access modifier on the `import` statement controls how they're exposed to 
> the rest of the file/project/world, and `private` is the default. It's a 
> little weird to have `private` be the default on `import` when `internal` is 
> the default on everything else, but the alternative is to change `import`'s 
> behavior in a way that is neither backwards-compatible, nor likely to be 
> correct.

Internal is the default.

> 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.

Doesn’t mean you can import internal APIs from modules you don’t own (that’s 
still banned).  You can still only export public API.

> 
>> 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.
> 
> Whoa, wait, what? Why? I think hiding parts of the implementation is a good 
> use case for re-exporting a module. And I think the clear implication of 
> attaching these clauses directly to the import statement is that it controls 
> how the module is imported everywhere that statement makes it visible, not 
> just within the one file. If we're not going to do that, I think these 
> clauses ought to be separated from the `import` statement and turned into 
> something separate.
> 
> -- 
> Brent Royal-Gordon
> Architechies
> 

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

Reply via email to