This continues to forbid use cases that are critical. For instance, I am writing a library that vends additional conformances for Float and Double. Any numerics library would need to do the same.
Your design would eliminate all such libraries, which is a non-starter. I am not sure what defects you are trying to solve with this proposal. On Sun, Apr 16, 2017 at 17:51 Howard Lovatt <[email protected]> wrote: > @Brent, > > I have updated the proposal to address your concerns, in particular I > don't see that retrospectively adding methods and protocols has been > removed it has just had its ugly corners rounded. See revised proposal > below particularly the end of section "Retrospectively adding protocols > and methods" and new section "Justification". > > Hope this convinces you that the change is worthwhile. > > -- Howard. > > ==================================== > > # Proposal: Split extension usage up into implementing methods and adding > methods and protocols retrospectively > > > ## Revision history > > | Version | Date | Comment | > > |---------|--------------|--------------| > > | Draft 1 | 11 April 2017 | Initial version | > > | Draft 2 | 13 April 2017 | Added support for post-hoc conformance to a > protocol - replaced static final extensions with final extensions | > > | Draft 3 | 17 April 2017 | Added justification section | > > > ## 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 retrospectively. > > > ## Implementing methods > > > If the extension is in the same file as the protocol/struct/enum/class > declaration then it implements the methods and is dispatched using a > Vtable. EG: > > > File P.swift > > protocol/struct/enum/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/enum/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/enumerated/classes you cannot declare in type and implement in > extensions. The proposal unifies the behaviour of > protocol/struct/enum/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 proposal 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. > > > ## Retrospectively 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/enum/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 is implicitly final, as the name > would suggest, and cannot be overridden. > > > Notes: > > > 1. If the final extension 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 adds the protocol. > > > 2. If the final extension adds a protocol then it must implement all the > methods in that protocol that are not currently implemented. > > > 3. If the final extension is outside of the file in which the > protocol/struct/enum/class declaration is in then the extension and the > methods can only have fileprivate or internal access. This prevents > retrospective extensions from numerous modules clashing, since they are not > exported outside of the module. > > > When a type is extended inside a module with a final extension the > extension is not exported. For example: > > > final extension Int: P2 { > > func m2P() { print("Int.m2P") } > > } > > > If an exported function uses Int, e.g.: > > > public func f(_ x: Int) -> Int { > > x.m2P() > > return x > > } > > > Then when used in an external module both the input Int and the output Int > are not extended with P2. However as the Int goes into f it gains P2 > conformance and when it leaves it looses P2 conformance. Thus inside and > outside the module the behaviour is easily understood and consistent and > doesn't clash with other final extensions in other modules. > > > Taking the above example further an Int with P2 conformance is required by > the user of a library; then it can simply and safely be provided, e.g.: > > > public class P2Int: P2 { > > var value = 0 > > func m2P() { print("Int.m2P") } > > } > > > This type, P2Int, is easy to write, one line longer than a final > extension, and can easily be used as both a P2 and an Int and does not > clash with another Int extension from another module. > > > ## Justification > > > The aim of Swift is nothing more than dominating the world. Using the > current, April 2017, https://www.tiobe.com/tiobe-index/ index of job > adverts for programmers the languages that are in demand are: Java 15.568%, > C 6.966%, C++ 4.554%, C# 3.579%, Python 3.457%, PHP 3.376%, Visual Basic > .NET 3.251%, JavaScript 2.851%, Delphi/Object Pascal 2.816%, Perl 2.413%, > Ruby 2.310%, and Swift 2.287%. So Swift at 12th is doing very well for a > new language and is already above Objective-C at 14th. However there is > obviously a long way to go and the purpose of this proposal is to help with > this climb. > > > A characteristic of many of the languages above Swift in the Tiobe Index > is that they have major third party libraries; for some languages they are > almost defined by their third part libraries, e.g. Ruby for Rails. A major > part of this proposal is to make extensions safe when using multiple > libraries from different venders. In particular final extensions are not > exported. > > > As part of Swift's goal of world domination is that it is meant to be easy > to learn by a process of "successive disclosure". The current inconsistent > behaviour of protocols and extensions hinders this process and is a common > gotcha for newbies. This proposal eliminates that problem also. > > > Extensions are not new in languages, they are part of the .NET languages > for example. Since .NET popularised extensions they have been discussed by > other language communities, particularly Java and Scala, and in the > academic community (normally termed the Expression Problem) however they > have not proved popular because of the problems they cause. Nearly all > languages have a strong bias towards keeping the language small and simple > and trade of the advantages of a feature against the disadvantages and the > feature only makes it into the language if it offers many advantages, has > few disadvantages, and is not heavily overlapping with other features. This > keeping it small and simple test is what extensions have failed in other > languages. > > > Experience from .NET can however be used to improve extensions. There is > some excellent advice > https://blogs.msdn.microsoft.com/vbteam/2007/03/10/extension-methods-best-practices-extension-methods-part-6/ > written by the VB .NET team when they added extensions to VB .NET. The > best-practice advice can be summarised by the following quotes from the > reference: > > > 0. "In most real world applications these suggestions [the rest of the > suggestions] can (and quite frankly should!) be completely ignored." This > is an important observations, in your own code that is not intended for > reuse; go for it, use extensions. The proposal importantly still allows > this style of programming and in fact improves it by adding consistent > behaviour and syntax between protocols/structs/enumerated/classes. > > > 1. "Read the .NET Framework Class Library Design Guidelines." The > equivalent for Swift is lacking at this stage. Probably because third party > libraries are rare. > > > 2. "Be wary of extension methods." This recommendation is formalised in > the proposal by limiting final extensions to be fileprivate or internal. > > > 3. "Put extension methods into their own namespace." This recommendation > is formalised in the proposal by limiting final extensions to be > fileprivate or internal. > > > 4. "Think twice before extending types you don’t own." > > > 5. "Prefer interface extensions over class extensions." Translation to > Swift terminology provide default implementations for protocol methods. The > proposal encourages this by eliminating a major gotcha with the current > implementation, namely the proposal always dispatches via a Vtable to give > consistent behaviour. > > > 6. "Be as specific with the types you extend as possible." Translation > to Swift terminology provide default implementations for protocol methods > that extend other protocols if there is a more specific behaviour that is > relevent. The proposal encourages this by eliminating a major gotcha with > the current implementation, namely the proposal always dispatches via a > Vtable to give consistent behaviour. > > > The proposal formalises these best practices from .NET whilst increasing > consistence and without loosing the ability to use extensions heavily in > your own one-off code to allow for rapid development. Most of the best > practices are for better libraries, particularly third party, which is an > important area for future Swift growth onto the server side. This proposal > actively encourages this transition to large formal server side code > without loosing the free wheeling nature of app code. > > > ## 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 > . > > > =================================================== > > > #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 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 14 Apr 2017, at 8:17 am, Brent Royal-Gordon <[email protected]> > wrote: > > On Apr 13, 2017, at 3:10 PM, Howard Lovatt via swift-evolution < > [email protected]> wrote: > > > I don't see that retroactive conformance needs to be exportable. If it is > exported then you cannot prevent clashes from two modules, this is a known > problem in C#. Because of this and other problems with C# extensions, this > style of extension were rejected by other language communities (notably > Java and Scala). > > > A better alternative for export is a new class that encapsulates the > standard type but with added methods for the protocol to be added. This way > there is no clash between modules. EG: > > > public protocol P { > > func m() -> String > > } > > public class PInt: P { > > var value = 0 > > func m() -> String { return "PI.m" } > > } > > > Howard, this would be very source-breaking and would fail to achieve > fundamental goals of Swift's protocol design. Removing retroactive > conformance is no more realistic than removing Objective-C bridging—another > feature which introduces various ugly edge cases and tricky behaviors but > is also non-negotiable. > > -- > Brent Royal-Gordon > Architechies > >
_______________________________________________ swift-evolution mailing list [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
