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

ilya ilya.nikokoshev at gmail.com
Mon Dec 7 16:59:40 CST 2015


Yep, my example contains an empty extension C:P - as you correctly notes,
that way it compiles.

I did make a mistake when writing the original email, but corrected it a
couple of seconds later, so I stand by the idea that this logic is
deduceable :)

In both cases the last line is calling something that can be denoted as  (C()
as P).P_f
Now, if we write the effects of the line marked "remember this line" in
pseudocode as

class C:P {
override P_f = C_f // effect of C:P if "remember" is present
}

the difference between cases is rather logical.
On Tue, Dec 8, 2015 at 01:42 Paul Cantrell <cantrell at pobox.com
<javascript:_e(%7B%7D,'cvml','cantrell at pobox.com');>> wrote:

> I must admit I'm having difficulty understanding why it's a big deal
> whether the dispatch will be static or dynamic. This seems like an
> implementation detail; any "dynamic dispatch" in the aforementioned sense
> can actually became static for a final class.
>
>
> It’s not just an implementation detail. The original article demonstrates
> this compellingly, I think.
>
> Yes, it’s true that, as an optimization, the compiler can choose to use
> static dispatch in situations where doing so makes no difference (e.g.
> calling a final method, calling a private method with no overrides, etc.).
>
> However, there are situations where static dispatch changes the behavior
> of the code. At that point, it’s a semantic difference. It’s those cases
> I’m concerned about.
>
> Just think about function defined as having a "magic prefix" that
> corresponds technically to vtable where they can be located:
>
>
> It’s not that it’s hard to understand what’s happening if you *already
> know* that a call uses static dispatch. The problem is that it’s
> difficult to determine *whether it does*.
>
> • • •
>
> Note that your example code with C_f and P_f does not demonstrate the
> problem at hand. It’s worth working through why.
>
> Translating your pseudocode into actual Swift, this does not compile:
>
>     class C {
>         func f() {
>             print("C_f")
>         }
>     }
>
>     protocol P {
>         func f()
>     }
>
>     extension C: P {
>         func f() {  // compiler error here
>             print("P_f")
>         }
>     }
>
> I imagine that you were thinking of something along these lines:
>
>     class C {
>         func f() {
>             print("C_f")
>         }
>     }
>
>     protocol P {
>         func f()  // remember this line
>     }
>
>     extension P {
>         func f() {
>             print("P_f")
>         }
>     }
>
>     extension C: P { }  // C: P now separate from extension impl of f()
>
>
> However, this does not behave as you think it does:
>
>     (C() as C).f()  // C_f
>     (C() as P).f()  // C_f
>
> However *again*, if you remove the line marked “remember this line,”
> *then* the code does do what you think it does:
>
>     (C() as C).f()  // C_f
>     (C() as P).f()  // P_f
>
> I’d say that if you got confused in the course of explaining how it's not
> confusing … well, that’s pretty good evidence that it is indeed confusing.
>
> Cheers,
>
> Paul
>
>
> On Dec 7, 2015, at 3:56 PM, ilya <ilya.nikokoshev at gmail.com
> <javascript:_e(%7B%7D,'cvml','ilya.nikokoshev at gmail.com');>> wrote:
>
>
>
> On Mon, Dec 7, 2015 at 7:17 AM, Paul Cantrell via swift-evolution <
> swift-evolution at swift.org
> <javascript:_e(%7B%7D,'cvml','swift-evolution at swift.org');>> wrote:
>
>> One of the few things in Swift 2 that feels to me like a design flaw is
>> the way Swift mixes static and dynamic method dispatch.
>>
>> Alexandros Salazar gives an excellent explanation of this problem — and I
>> agree wholeheartedly with his title for the article:
>>
>>     http://nomothetis.svbtle.com/the-ghost-of-swift-bugs-future
>>
>> The upshot is that when we see this:
>>
>>     foo.bar()
>>
>> …it’s very hard to know how the compiler will determine which
>> implementation of bar() to use. It might use static dispatch; it might use
>> dynamic dispatch.
>>
>> The rules that govern this are arcane, and hard to remember. They have
>> the feeling of being a “gotcha” question for job interviews — always a red
>> flag for language features.
>>
>> Even if you remember the rules, the information needed to determine
>> whether dispatch is static or dynamic is hard to track down. It depends on
>> whether bar()’s implementation comes from an extension, whether the
>> extension method appeared on the extended protocol, and whether the
>> inferred type of foo is the protocol itself or an implementing type.
>>
>> A crucial part of the meaning of “foo.bar()” is implicit, and hard to
>> determine.
>
>
> I must admit I'm having difficulty understanding why it's a big deal
> whether the dispatch will be static or dynamic. This seems like an
> implementation detail; any "dynamic dispatch" in the aforementioned sense
> can actually became static for a final class.
>
> I understand there can be a confusion about the method called when the
> protocol contains a method implementation, but there are some simple ways
> to understand why things work as they do. Just think about function defined
> as having a "magic prefix" that corresponds technically to vtable where
> they can be located:
>
> class C {
>    func C_f // declares C.C_f
> }
>
> protocol P {
>   func P_f
> }
>
> // implementation of P.P_f
>
> extension C:P {
>   // declares that C.C_f = C.P_f
> }
>
> (C() as C).f -> calls C.C_f = C.P_f
> (C() as P).f -> calls P.P_f
>
>
>> This runs contrary to Swift’s stated goal of prioritizing clarity at the
>> point of API use, and its general pattern of making intent explicit. And it
>> feels dangerous — a wellspring of insidious bugs.
>>
>> Thus:
>>
>>
>> PROPOSAL
>>
>> Make the syntax “foo.bar()” always use dynamic dispatch, i.e. always use
>> _only_ the runtime type of foo to determine which implementation of bar()
>> to use. If an extension method collision occurs when a type implements
>> multiple protocols, require the type to explicitly specify which one to use
>> (as Swift already requires the caller to do at the point of invocation).
>>
>>
>> I mean this proposal somewhat as a strawman. It’s such an obvious choice,
>> I’m sure there were good reasons not to do it. But I’d like to propose the
>> obvious solution in order to understand what’s wrong with it. I realize
>> static dispatch precludes some optimizations, but I doubt that this alone
>> drove the design choice. I see no safety or expressiveness upside to the
>> way it works now.
>>
>> Cheers,
>>
>> Paul
>>
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org
>> <javascript:_e(%7B%7D,'cvml','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/20151208/32a62d30/attachment.html>


More information about the swift-evolution mailing list