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
    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
    }
    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.

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

Reply via email to