[swift-evolution] Proposal: Split extensions into implementing methods and adding static functions Was: [swift-evolution-announce] [Review] SE-0164: Remove final support in protocol extensions

Xiaodi Wu xiaodi.wu at gmail.com
Mon Apr 10 22:26:47 CDT 2017


As far as I'm aware, eliminating retroactive conformances is a non-starter.


On Mon, Apr 10, 2017 at 21:44 Howard Lovatt <howard.lovatt at 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.wu at gmail.com> wrote:
>
> On Mon, Apr 10, 2017 at 5:35 PM, Howard Lovatt via swift-evolution <
> swift-evolution at 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_rose at 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 at 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 at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170411/c32ab686/attachment.html>


More information about the swift-evolution mailing list