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

Joe Groff jgroff at apple.com
Mon Dec 7 15:20:59 CST 2015


> On Dec 7, 2015, at 1:16 PM, Paul Cantrell <cantrell at pobox.com> wrote:
> 
> Thanks, Joe, this is helpful. Thinking through the problem of extensions from separate modules more carefully, I see where my strawman proposal falls apart.
> 
> Thinking more carefully about my concerns, they boil down to two separate things:
> 
> 1. The current static dispatch behavior is confusing, a likely source of unintended behavior & thus of bugs.
> 
> 2. Even with perfect understanding of the language, it’s (I think?) currently not possible to introduce a new extension method at multiple places the protocol hierarchy and make guarantees about subtype-appropriate behavior.

This is true today. I don't think it's a fundamental constraint we intend to keep in place forever.

> 
>> - the ability for extensions to protocols to add new dynamically-dispatched methods, and
> 
> This would completely address my concern #1. It would make it possible to fix Alexandros’s example.
> 
> But doesn’t this reintroduce the “monkey collision” problem? Couldn’t two modules independently add new dynamically dispatched methods that collide when both modules are imported?

It's helpful to think of method names as being namespaced in Swift, by both their enclosing module and type. If two modules independently extend a protocol with a method of the same name, you still semantically have two distinct methods that dispatch independently. The extension would have to be factored into a common module both modules see for them to interact.

-Joe

> 
>> - compiler quality work to diagnose confusing cases where concrete types obviously shadow non-dynamic protocol extensions.
> 
> That would be a nice thing to have in Swift 2.2.
> 
>> There is a related behavior change proposal Doug Gregor's been working on for classes, to have conforming to a protocol implicitly re-declare all the protocol's methods as class methods, so that they can be overridden by subclasses in the expected way.
> 
> That seems entirely sensible.
> 
> Cheers,
> 
> Paul
> 
> 
>> On Dec 7, 2015, at 2:14 PM, Joe Groff <jgroff at apple.com> wrote:
>> 
>> The main reason to constrain dynamic dispatch is modularity. The problems of interfering categories or monkey-patches in languages like ObjC and Ruby with late-bound dispatch and open classes are well-known. In Swift's model, it isn't possible for an extension in one module to interfere with existing protocol conformances or class hierarchies at runtime (except with @objc of course). Modules need to be compiled together to be aware of each other to interact with each other's interfaces.
>> 
>> The particular example Alexandros brings up is more an artifact of our existing implementation than desirable behavior. Two key things are missing:
>> 
>> - the ability for extensions to protocols to add new dynamically-dispatched methods, and
>> - compiler quality work to diagnose confusing cases where concrete types obviously shadow non-dynamic protocol extensions.
>> 
>> There is a related behavior change proposal Doug Gregor's been working on for classes, to have conforming to a protocol implicitly re-declare all the protocol's methods as class methods, so that they can be overridden by subclasses in the expected way.
>> 
>> -Joe
>> 
>>> On Dec 6, 2015, at 8:17 PM, 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. 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
>> 
> 



More information about the swift-evolution mailing list