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. 

> On 13 Apr 2017, at 10:31 pm, Xiaodi Wu <[email protected]> wrote:
> 
> 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 <[email protected]> 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 <[email protected]> wrote:
>>> 
>>> As far as I'm aware, eliminating retroactive conformances is a non-starter.
>>> 
>>> 
>>>> On Mon, Apr 10, 2017 at 21:44 Howard Lovatt <[email protected]> 
>>>> 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 <[email protected]> wrote:
>>>>> 
>>>>> On Mon, Apr 10, 2017 at 5:35 PM, Howard Lovatt via swift-evolution 
>>>>> <[email protected]> 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 <[email protected]> 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 
>>>>>>>> <[email protected]> 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
>>>>> [email protected]
>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>>> 
>>>>> 
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to