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