On Mon, Jul 18, 2016 at 4:49 PM, Robert Widmann <[email protected]> wrote:
> > On Jul 18, 2016, at 2:32 PM, Xiaodi Wu <[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`? For you, does `MyFoo` conform to `Swift.FooProtocol`? Can you call `MyFoo.frobnicate()`? How about `MyFoo.bobnicate()`? What if you try to subclass `MyFoo`? Does your subclass still conform to `Swift.FooProtocol`? Do you override `bobnicate()` or `frobnicate()`? My head hurts... > > > On Mon, Jul 18, 2016 at 16:10 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. >> >> 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.mainQueueimport 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 {} >> @_versionedinternal 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.Datepublic 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] >> https://lists.swift.org/mailman/listinfo/swift-evolution >> > >
_______________________________________________ swift-evolution mailing list [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
