> is changed so that, outside the module, it has a member `frobnicate()` and > no longer has a member `bobnicate()`?
*Unchanged. Other than that, you’ve got it! I wanted to make *very* sure that whatever APIs come in are the same ones that go out regardless of local changes. > On Jul 18, 2016, at 3:17 PM, Xiaodi Wu <[email protected]> wrote: > > On Mon, Jul 18, 2016 at 5:09 PM, Robert Widmann <[email protected] > <mailto:[email protected]>> wrote: > >> On Jul 18, 2016, at 3:00 PM, Xiaodi Wu <[email protected] >> <mailto:[email protected]>> wrote: >> >> On Mon, Jul 18, 2016 at 4:49 PM, Robert Widmann <[email protected] >> <mailto:[email protected]>> wrote: >> >>> On Jul 18, 2016, at 2:32 PM, Xiaodi Wu <[email protected] >>> <mailto:[email protected]>> wrote: >>> >>> This is an interesting document. I think it deserves careful study. For >>> now, some questions: >>> >>> What is the rationale behind permitting the using of specific methods? This >>> seems to be usually fine-grained in comparison to other languages. What use >>> cases do you have in mind for this? >>> >> >> One use case: Swift libraries export not just member references as I’ve used >> here, but a large amount of free functions. It has long been a problem that >> free functions seem to pollute a shared namespace and there didn’t seem to >> be a clear way to hide them. >> >> Would a plausible simplification of the proposal be to have it fine-grained >> enough to address free functions but not methods inside types? >> Incidentally, although I do not see it in the proposal, I assume that * in >> some form will be permitted (as in, `import Foundation using *`). >> >> >>> I can see the use case for hiding specific symbols when they come into >>> conflict with your own, but in your example you're hiding specific methods >>> declared *in* an imported type. What is the use case here? Is it going to >>> allow me to open backdoors so that, if I don't like `Foo.frobnicate()`, I >>> can hide it and then substitute my own in an extension? This seems like a >>> bad thing at first blush. >> >> For members that would be an acceptable use-case. The worst-case scenario >> that comes to mind is this being used as a way to “virtually override” a >> method in a subclass. Then again, the scope of the damage is limited to the >> file in which you’ve declared this monstrosity so clients and even you will >> not be able to see it outside of there unless you explicitly redeclare the >> hiding import (in which case, you probably know what you’re doing). >> >> A use care here might be hiding the KVO-ish parts of an object from >> yourself, or more generally subsetting out the part of an API you know you >> shouldn’t interact with in a particular submodule. >> >>> >>> I can see the obvious use case for renaming modules and types on >>> import--basically, in my mind, it's like typealiases with hiding, and it's >>> available in other languages of course. But how would renaming methods >>> work? If Foo conforms to Equatable and I rename `Foo.==` to `Foo.!=`, is >>> the type I import still Equatable? How would it behave? And even if Foo is >>> fine, what happens if I try to subclass my Frankensteinian Foo? >> >> Of course you still conform to Equatable. The renaming defines a mapping >> from your names to “proper" names. For example, if you use a renaming >> import to change the requirements of a protocol in a file, then your >> conformance will simply look at the mapping and see that everything resolves >> into its proper place. Bear in mind that your renamings will not survive >> outside of the file in which you declare them. Frankenteinian Foo exists >> where you say it does and nowhere else. Everybody else just sees Foo >> conform to Equatable (unless they rename things themselves). >> >> Maybe let's work through an example: >> >> Suppose we have in stdlib: >> >> ``` >> public protocol FooProtocol { >> func frobnicate() >> } >> ``` >> >> Now, I write a library: >> >> ``` >> import Swift.FooProtocol renaming (FooProtocol.frobnicate(), to: >> FooProtocol.bobnicate()) >> >> public open class MyFoo : Swift.FooProtocol { >> public open func bobnicate() { >> print("Does your head hurt yet?") >> } >> } >> ``` >> >> Now, you are an end user of my sinister library. >> >> What is the public API of `MyFoo`? > > The proposal addresses this > > > 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. > > The user (and even you in other files that import this module) will see a > protocol conformance exactly as laid out in the Swift.FooProtocol module. > >> For you, does `MyFoo` conform to `Swift.FooProtocol`? > > It conforms because the renaming you wrote describes a way of resolving > FooProtocol.bobnicate() (your API) to FooProtocol.frobnicate() (everybody > else’s API). > >> Can you call `MyFoo.frobnicate()`? How about `MyFoo.bobnicate()`? >> >> What if you try to subclass `MyFoo`? > > If you are inside the module you wrote the renaming, you will use it. If you > are outside of it, you will see the protocol requirement sans renaming. > >> Does your subclass still conform to `Swift.FooProtocol`? >> Do you override `bobnicate()` or `frobnicate()`? >> My head hurts… > > Because you have explicitly renamed the protocol requirement, you will > override the same protocol requirement both inside and outside this module > but your renaming will not propagate to other files unless they themselves > opt in the way you have here. It would be particularly sinister if you could > arbitrarily edit the user-facing API of members simply by importing a library. > > Sounds good. If I understand you correctly, by conforming `MyFoo` to an > internally renamed `Swift.FooProtocol`, the renaming of the user-facing API > for `FooProtocol` means that the public API of `MyFoo` is changed so that, > outside the module, it has a member `frobnicate()` and no longer has a member > `bobnicate()`? > > >> >> >>> >>> On Mon, Jul 18, 2016 at 16:10 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 <>) 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.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.<<<<<) >>> >>> <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
