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

ilya ilya.nikokoshev at gmail.com
Mon Dec 7 15:56:47 CST 2015


On Mon, Dec 7, 2015 at 7:17 AM, Paul Cantrell via swift-evolution <
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
> 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/b0a48fd8/attachment.html>


More information about the swift-evolution mailing list