[swift-evolution] Proposal: Universal dynamic dispatch for method calls

Kevin Ballard kevin at sb.org
Wed Dec 9 15:41:43 CST 2015


On Wed, Dec 9, 2015, at 01:28 PM, Gwendal Roué wrote:
> I quite knew about the caveat you’re talking about. This is what had
> prevented me from shipping this technique in the public API yet. I
> still need thinking.
>
> And my current thinking is that this case is unlikely to happen (in my
> particular case, at least). Further, I’d rather document against this
> usage or even cutting off this feature, rather than adding this extra
> _f function. Talk about a simple API, when the library users needs a
> PhD in Swift dispatch subtleties to properly validate a poor struct
> before storing it in the database. That’s not my ambition when I write
> a library.

I don't understand. Why does the _f() approach impact library users at
all? The only impact it should have on them is showing extra functions
in the code completion list, but I seem to recall seeing some reference
to doc comment changes being added to Swift to let you influence code
completion by indicating that some other function should always be
preferred to this one (e.g. so you can mark _f() as saying that users
should always call f() instead).

The only code that needs to care about the distinction between f() and
_f() is in implementations of the protocol P, and I don't see `_f()` as
being any worse than `(self as P).f()`. If anything, it's better because
it doesn't require the code reader to know whether f() was declared in
the protocol (and is therefore subject to overriding).

> I hope, nevertheless, that I had shown:
>
> 1. that protocol default implementation can actually be "overriden",
>    even if it is dangerous. If this danger can not be alleviated, then
>    this is a hole in the language, and this hole may well need to be
>    fixed because the inadequate usage I’ve show, should it reveal
>    actually improper, will be discovered by others developers.

I disagree that this is a hole in the language. You aren't actually
overriding anything in your version, you're just providing a method of
the same name on the type itself, and according to the Swift language
rules if someone calls the method on your type it will prefer your
type's version instead of the protocol (this is just due to normal
overloading behavior, where your type's method is considered more
specific than the protocol and is therefore preferred). And I also think
that the current behavior is the correct  model; it would be genuinely
surprising to me to have methods defined only in extensions actually be
overridden by a concrete type when I'm calling the method on the
protocol (as opposed to on the concrete type).

> 2. that there is a quite valid use case for letting adopting types
>    "override" the default implementation of their protocol. The _f
>    extra method is a lacking workaround.

Well yes, that's kind of the whole point of protocols, isn't it? The
problem is you didn't define the method in the protocol. If you want a
type to be able to override it, then you have to define it in the
protocol. If you don't want it to be able to override it, then you
define it in an extension.

I'm also a little confused here, because fixing this "hole" would break
your code, as you'd no longer be able to get at the default
implementation. The only way to recover that behavior, short of
inventing brand new syntax, is to use the exact same _f() workaround I
just suggested.

-Kevin Ballard

>> Le 9 déc. 2015 à 22:13, Kevin Ballard <kevin at sb.org> a écrit :
>>
>> That is nice, but if someone writes a method that's generic over <T:
>> P> then your "override" won't get called. Seems like it's better to
>> structure it like
>>
>> protocol P {    func f() } extension P {    /// Default
>> implementation for `f`. Calls through to `_f()`.    func f() { _f() }
>> /// Helper that provides the base functionality for `f`.    func _f()
>> { ... } } struct S {    func f() {        ...        _f()    } }
>>
>> This way you can write code that's generic over <T: P> (or that takes
>> a P object directly) and it will still call the overrides.
>>
>> -Kevin Ballard
>>
>> On Wed, Dec 9, 2015, at 11:01 AM, Gwendal Roué wrote:
>>>
>>>> Le 9 déc. 2015 à 19:52, Kevin Ballard via swift-evolution <swift-
>>>> evolution at swift.org> a écrit :
>>>>
>>>> b) methods defined in protocol extensions by definition can't be
>>>>    overridden already,
>>>
>>> Methods defined in protocol extension actually can, sort of, be
>>> overridden, and this is very useful:
>>>
>>> protocol P { }    extension P {        func f() { … }    }    struct
>>> S {        func f() {            ...            (self as P).f()
>>> …        }    }
>>>
>>> I know only one use case for this technique, in the groue/GRDB.swift
>>> SQLite wrapper:
>>>
>>> In this library, a DatabasePersistable protocol provides basic CRUD
>>> operations, and a Record class adopts this protocol and "overrides"
>>> with the technique above the protocol methods with extra features
>>> provided by the class (especially change tracking).
>>>
>>> The benefits of this architecture are:
>>>
>>> - You can subclass use the full-featured Record base class, and get
>>>   CRUD + change tracking for free.
>>> - The Record subclasses can override the CRUD methods, and add
>>>   custom code (validation, for example).
>>> - You can have a custom struct adopt DatabasePersistable, and get
>>>   CRUD for free.
>>> - The custom structs that can also "override" the CRUD methods, and
>>>   add custom code (validation, for example).
>>>
>>> This is, in my opinion, a very valid use case for this "overriding".
>>> Gwendal Roué
>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20151209/41649bd9/attachment.html>


More information about the swift-evolution mailing list