The retroactive conformance needs to be exportable. If one cannot vend a library that conforms standard library types to new protocols, then that is a non-starter.
On Thu, Apr 13, 2017 at 06:07 Howard Lovatt <howard.lov...@gmail.com> wrote: > @Xiaodi, > > You can safely post-hoc add protocols and methods provided that they are > final, do not override, and are not exported from the module. See version 2 > of the proposal below. > > -- Howard. > > # Proposal: Split extension usage up into implementing methods and adding > methods and protocols post-hoc > > > Draft 2 (Added support for post-hoc conformance to a protocol - replaced > static final extensions with final extensions) > > > ## Introduction > > > Currently extension methods are confusing because they have different > dispatch rules for the same calling syntax. EG: > > > public protocol P { > > func mP() -> String > > } > > extension P { > > func mP() -> String { return "P.mP" } > > func mE() -> String { return "P.mE" } > > } > > struct S: P { > > func mP() -> String { return "S.mP" } > > func mE() -> String { return "S.mE" } > > } > > let s = S() > > s.mP() // S.mP as expected > > s.mE() // S.mE as expected > > let p: P = s // Note: s now typed as P > > p.mP() // S.mP as expected > > p.mE() // P.mE unexpected! > > > Extension methods can also cause compatibility problems between modules, > consider: > > > In Module A > > extension Int: P { > > func m() -> String { print("A.m") } > > } > > > In Module B > > extension Int: P { > > func m() -> String { print("B.m") } > > } > > > In Module C > > import A > > import B // Should this be an error > > let i = 0 > > i.m() // Should it return A.m or B.m? > > > This proposal cures the above two problems by separating extension methods > into two seperate use cases: implementations for methods and adding methods > and protocols post-hoc. > > > ## Implementing methods > > > If the extension is in the same file as the protocol/struct/class > declaration then it implements the methods and is dispatched using a > Vtable. EG: > > > File P.swift > > protocol/struct/class P { > > // func m() not declared in type since it is added by the > extension, under this proposal it is an error to include a declaration in a > type *and* in an extension > > } > > extension P { > > func m() { print("P.m") } // m is added to the > protocol/struct/class declaration > > } > > > Same or other file > > struct S: P { > > override func m() { print("S.m") } // Note override required > because m already has an implementation from the extension > > } > > let p: P = S() // Note typed as P > > p.m() // Now prints S.m as expected > > > Extensions in the same file as the declaration can have any access, can be > final, and can have where clauses and provide inheritable implementations. > > > In a protocol at present there is a difference in behaviour between a > protocol that declares a method that is then implemented in an extension > and a protocol that just has the method implemented in an extension and no > declaration. This situation only applies to protocols, for structs and > classes you cannot declare in type and implement in extensions. The > proposal unifies the behaviour of protocol/struct/class with extensions and > prevents the error of a minor typo between the protocol and extension > adding two methods instead of generating an error. > > > The implementation needed to achieve this is that a value instance typed > as a protocol is copied onto the heap, a pointer to its Vtable added, and > it is passed as a pointer. IE it becomes a class instance. No change needed > for a class instance typed as a protocol. > > > ## Post-hoc adding protocols and methods > > > A new type of extension is proposed, a "final extension", which can be > either in or outside the file in which the protocol/struct/class > declaration is in. EG: > > > protocol P2 { > > func m2P() > > } > > final extension S: P2 { // Note extension marked final > > func m2P() { print("SP2.m2P") } // Implicitly final, completely > implements P2 > > func m2E() { print("SP2.m2E") } // Implicitly final, not an > existing method > > } > > > Which are called as any other method would be called: > > > let s = S() > > s.m2P() // Prints SP2.m2P > > s.m2E() // Prints SP2.m2E > > > A method added by a final extension is implicitly final, as the name would > suggest, and cannot be overridden. > > > If the final extension: > > > 1. Adds a method, e.g. m2E, that method cannot already exist. IE a final > extension cannot override an existing method or implement a protocol > declared method that lacks an implementation unless it also post-hoc adds > the protocol. > > > 2. Adds a protocol then it must implement all the methods in that > protocol that are not currently implemented. > > > 3. Is outside of the file in which the protocol/struct/class declaration > is in then the extension and the methods can only have fileprivate or > internal access. This prevents post-hoc extensions from numerous modules > clashing, since they are not exported outside of the module. > > > ## Possible future work (not part of this proposal) > > > This proposal will naturally allow bodies to be added to protocols > directly rather than via an extension, since under the proposal the > extension adds the declaration to the type so it is a small step to allow > the protocol methods to have an implementation. > > > In an opposite sense to the above adding bodies to protocols, extensions > could be allowed to add method declarations without bodies to protocols. > > > The two above future work proposals, if both added, would add symmetry to > where declarations and bodies may appear for protocols. > > > ## In summary. > > > The proposal formalises the split use of extensions into their two uses: > implementing methods and post-hoc adding protocols and methods. Syntax is > added that clarifies the two use cases, the former are termed extensions > and must be in the same file as the type is declared, and the latter are > termed final extensions and can be in any file, however if they are not in > the type's file the they can only have fileprivate or internal access. > > > Note the distinction between an extension in the same file and in a > separate file is consistent with the philosophy that there is special > status to the same file as proposed for private in > https://github.com/apple/swift-evolution/blob/master/proposals/0169-improve-interaction-between-private-declarations-and-extensions.md > . > > On 11 Apr 2017, at 1:26 pm, Xiaodi Wu <xiaodi...@gmail.com> wrote: > > As far as I'm aware, eliminating retroactive conformances is a non-starter. > > > On Mon, Apr 10, 2017 at 21:44 Howard Lovatt <howard.lov...@gmail.com> > wrote: > >> @Xiaodi, >> >> You make two drugs. >> >> 1. Deliberately making retroactive conformance outside of the file in >> which the type is declared illegal because of the problems it causes. See >> all the questions on Swift Users and watch people learning Swift get caught >> out. >> >> 2. Outside of the file in which the type is declared the static final >> extension is restricted to internal or fileprivate so that multiple modules >> can add static final extensions without clashes. >> >> >> -- Howard. >> >> On 11 Apr 2017, at 8:51 am, Xiaodi Wu <xiaodi...@gmail.com> wrote: >> >> On Mon, Apr 10, 2017 at 5:35 PM, Howard Lovatt via swift-evolution < >> swift-evolution@swift.org> wrote: >> >> In response to Jordan Rose's comment I suggest the following change: >> >> Proposal: Split extension usage up into implementing methods and adding >> static functions >> >> >> Currently extension methods are confusing because they have different >> dispatch rules for the same syntax. EG: >> >> >> protocol P { >> >> func m() >> >> } >> >> extension P { >> >> func m() { print("P.m") } >> >> } >> >> struct S: P { >> >> func m() { print("S.m") } >> >> } >> >> val p: P = S() // Note typed as P >> >> p.m() // Surprisingly prints P.m even though S implements its own m >> >> >> This is incorrect. This prints "S.m", not "P.m". >> >> >> val s = S() // Note typed as S >> >> s.m() // Prints S.m as expected >> >> >> This proposal cures the above problem by separating extension methods >> into two seperate use cases: implementations for methods and adding static >> functions. >> >> >> First implementing methods. >> >> >> If the extension is in the same file as the protocol/struct/class >> declaration then it implements the methods and is dispatched using a >> Vtable. EG: >> >> >> File P.swift >> >> protocol/struct/class P { >> >> func m() >> >> } >> >> extension P { >> >> func m() { print("P.m") } >> >> } >> >> >> Same or other file >> >> struct S: P { >> >> override func m() { print("S.m") } // Note override required >> because m already has an implementation from the extension >> >> >> Requiring `override` breaks retroactive conformance of types to >> protocols. This idea has been brought up over half a dozen times. Each time >> it fails in not being able to accommodate retroactive conformance. >> >> >> } >> >> val p: P = S() // Note typed as P >> >> p.m() // Now prints S.m as expected >> >> >> Extensions in the same file as the declaration can have any access, can >> be final, and can have where clauses and provide inheritable >> implementations. >> >> >> The implementation needed to achieve this is that a value instance typed >> as a protocol is copied onto the heap, a pointer to its Vtable added, and >> it is passed as a pointer. IE it becomes a class instance. No change needed >> for a class instance typed as a protocol. >> >> >> The second use case is adding static functions. >> >> >> A new type of extension is proposed, a static final extension, which can >> be either in or outside the file in which the protocol/struct/class >> declaration is in. EG: >> >> >> static final extension P { // Note extension marked static final >> >> func m() { print("P.m") } >> >> } >> >> >> Which is called as any other static function would be called: >> >> >> val s = S() >> >> P.m(s) // Prints P.m as expected >> >> >> The new static final extension is shorthand, particularly in the case of >> multiple functions, for: >> >> >> extension P { >> >> static final func m(_ this: P) { print("P.m") } >> >> } >> >> >> If the static final extension is outside of the file in which the >> protocol/struct/class declaration is in then the extension and the methods >> can only have fileprivate and internal access. >> >> >> What is the use case for having this restriction? What is the problem you >> are trying to solve? >> >> >> >> As at present protocol/struct/class can have both a static and instance >> method of the same name, m in the case of the example, because the usage >> syntax is distinct. As at present, static final extensions, both the >> extension and the individual functions, can have where clauses. >> >> >> In summary. >> >> >> The proposal formalises the split use of extensions into their two uses: >> implementing methods and adding static functions. Syntax is added that >> clarifies both for declarations and usage which type of extension is >> provided/in use. >> >> >> Note the distinction between an extension in the same file and in a >> separate file is consistent with the proposed use of private in >> https://github.com/apple/swift-evolution/blob/master/proposals/0169-improve-interaction-between-private-declarations-and-extensions.md >> . >> >> Comments? >> >> -- Howard. >> >> On 7 Apr 2017, at 4:49 am, Jordan Rose <jordan_r...@apple.com> wrote: >> >> [Proposal: >> https://github.com/apple/swift-evolution/blob/master/proposals/0164-remove-final-support-in-protocol-extensions.md >> ] >> >> On Apr 5, 2017, at 16:15, Howard Lovatt via swift-evolution < >> swift-evolution@swift.org> wrote: >> >> The review of SE-0164 "Remove final support in protocol extensions" >> >> >> - What is your evaluation of the proposal? >> >> The present situation isn't great. People get confused about which method >> will called with protocol extensions. Seems like every week there is a >> variation on this confusion on Swift Users mailing list. Therefore >> something needs to be done. >> >> However I am not keen on this proposal since it makes behaviour >> inconsistent between methods in protocol extensions, classes, and structs. >> >> I think a better solution would be one of the following alternatives: >> >> 1. Must use final and final means it cannot be overridden; or >> 2. If not final dispatches using a table like a class and if marked >> final cannot be overridden and if marked dynamic uses obj-c dispatching; or >> 3. Must be marked dynamic and uses obj-c dispatching. >> >> My preference would be option 2 but I think any of the three is superior >> to the present situation or the proposal. >> >> >> People have suggested all of these before, but none of them are obviously >> correct. It's true that we have a difference between extension members that >> satisfy requirements and those that don't, and that that confuses people. >> However, an extension-only member of one protocol can be used to satisfy >> the requirements of another protocol today, which is a tool for code reuse. >> >> (I *think* we managed to convince everyone that it's just a bug that a >> protocol extension method that satisfies a requirement cannot be overridden >> in a subclass, so at least that isn't an issue on top of the rest of this.) >> >> Oh, and we can't retroactively add members of a protocol extension to >> existing adopters, which is why protocol extension members cannot be @objc. >> There are limited circumstances where that would be safe, but that would be >> a separate proposal. >> >> Jordan >> >> >> _______________________________________________ >> swift-evolution mailing list >> swift-evolution@swift.org >> https://lists.swift.org/mailman/listinfo/swift-evolution >> >> >>
_______________________________________________ swift-evolution mailing list swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution