Proposal: Universal dynamic dispatch for method calls


(Paul Cantrell) #1

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


(Joe Groff) #2

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@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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Roy Fu) #3

Hmmmm…. interesting point.
I probably didn’t catch the point quite well
when writing obj-c or ruby or etc (I mean smalltalk), we used to focus on the Instances at runtime.
however, now, we should rather focus on the Types at declaration much much more. (yes, I mean inheritance is not a good idea most of the time)
so, I’d rather prefer to just unify simple Protocol as Types and the Protocols with associated Types when higher order Types is added (
e.g.
extension SequenceType<T>:Functor {
}
)

···

On Dec 7, 2015, at 12:17 PM, Paul Cantrell via swift-evolution <swift-evolution@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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(ilya) #4

Could you provide an example? I tried playing with protocol extensions,
modelling this situation:

If an extension method collision occurs when a type implements multiple

protocols,

but so far all examples I found where one would see a collision are
disallowed by compiler, e.g.

protocol P1 {
    func something() -> Int
}

protocol P2 {
    func something() -> Int
    func another() -> Int
}

extension P1 {

    func something() -> Int {
        return 1
    }

    func another() -> Int {
        return 1
    }
}

extension P2 {

    func something() -> Int {
        return 2
    }

    func another() -> Int {
        return 2
    }
}

class C1: P1 { // <- P1, P2 here doesn't compile

    func something() -> Int {
        return 0
    }

}

class C2: P2 {

}

class C3: P1, P2 {

    func something() -> Int {
        return 0
    }

    func another() -> Int {
        return 0
    }

}

···

On Mon, Dec 7, 2015 at 7:17 AM, Paul Cantrell via swift-evolution < swift-evolution@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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(ilya) #5

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

···

On Mon, Dec 7, 2015 at 7:17 AM, Paul Cantrell via swift-evolution < swift-evolution@swift.org> wrote:

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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Lily Ballard) #6

I'd love to see these two key things that Joe mentioned get added.
Diagnostics around confusing situations is a good idea (although there
needs to be a way to disable it too; what if I intentionally need to use
the same name in a type and in an extension to one of the conformed-to
protocols, for various reasons?).

I can't support the original proposal though, of making foo.bar() use
dynamic dispatch. The ability to use static dispatch in most cases is
one of the great features of Swift, and it's something that needs to be
preserved, not relegated to being an optimization that can be done
occasionally.

It's also worth noting that the current behavior, where methods defined
in protocol extensions can't be overridden by the implementing type, may
actually be the desired behavior in many cases. Heck, the ability to
trigger this different behavior is one of the three reasons to even
define methods in protocol extensions instead of in the protocol (the
others being when you're defining it for a subset of protocol
implementations, and when you're defining extensions on protocols
imported from other modules). Removing this functionality (by making
foo.bar() always dynamic) means reverting back to the Swift 1.x
convention of having global functions to provide this behavior.

I wouldn't mind seeing something like the `dynamic` keyword to allow
concrete types to override these protocol extension methods (although
`dynamic` in this case would not imply @objc, like it does today, which
may be a little confusing), but that would be opt-in behavior.

-Kevin Ballard

···

On Mon, Dec 7, 2015, at 12:14 PM, Joe Groff via swift-evolution 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@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@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Paul Cantrell) #7

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.

- 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?

- 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@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@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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Paul Cantrell) #8

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@gmail.com <mailto:ilya.nikokoshev@gmail.com>> wrote:

On Mon, Dec 7, 2015 at 7:17 AM, Paul Cantrell via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@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@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(ilya) #9

Yes, I read the article, even after I replied correctly to the "If yes, and
you understand why, congratulations; you don’t need to read any further."
challenge :slight_smile:

Remove all methods from P1 and P2, so that something() and another()

appear only in extensions.

Nope, I still don't see any problem:

protocol P1 {}
protocol P2 {}

extension P1 {

    func something() -> Int {
        return 1
    }

    func another() -> Int {
        return 1
    }
}

extension P2 {

    func something() -> Int {
        return 2
    }

    func another() -> Int {
        return 2
    }
}

class C1: P1, P2 {

    func something() -> Int {
        return 0
    }

}

C1().something() // class implementation, works as is
// C1().another() - ambigous, doesn't compile

PS I noticed you replied to me personally, perhaps it's a good idea to copy
to the list back.

···

On Tue, Dec 8, 2015 at 12:40 AM, Paul Cantrell <cantrell@pobox.com> wrote:

Remove all methods from P1 and P2, so that something() and another()
appear only in extensions.

Did you read Alexandros’s article I linked to in the OP? That’s the place
to start.

P

On Dec 7, 2015, at 2:52 PM, ilya <ilya.nikokoshev@gmail.com> wrote:

Could you provide an example? I tried playing with protocol extensions,
modelling this situation:

> If an extension method collision occurs when a type implements multiple
protocols,

but so far all examples I found where one would see a collision are
disallowed by compiler, e.g.

protocol P1 {
    func something() -> Int
}

protocol P2 {
    func something() -> Int
    func another() -> Int
}

extension P1 {

    func something() -> Int {
        return 1
    }

    func another() -> Int {
        return 1
    }
}

extension P2 {

    func something() -> Int {
        return 2
    }

    func another() -> Int {
        return 2
    }
}

class C1: P1 { // <- P1, P2 here doesn't compile

    func something() -> Int {
        return 0
    }

}

class C2: P2 {

}

class C3: P1, P2 {

    func something() -> Int {
        return 0
    }

    func another() -> Int {
        return 0
    }

}

On Mon, Dec 7, 2015 at 7:17 AM, Paul Cantrell via swift-evolution < > swift-evolution@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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(ilya) #10

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 :slight_smile:

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@pobox.com <javascript:_e(%7B%7D,'cvml','cantrell@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@gmail.com > <javascript:_e(%7B%7D,'cvml','ilya.nikokoshev@gmail.com');>> wrote:

On Mon, Dec 7, 2015 at 7:17 AM, Paul Cantrell via swift-evolution < > swift-evolution@swift.org > <javascript:_e(%7B%7D,'cvml','swift-evolution@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@swift.org
<javascript:_e(%7B%7D,'cvml','swift-evolution@swift.org');>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Joe Groff) #11

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

···

On Dec 7, 2015, at 1:16 PM, Paul Cantrell <cantrell@pobox.com> wrote:

- 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@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@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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Paul Cantrell) #12

- 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.

That seems like exactly how it should work. I even start typing up some thoughts on per-module namespacing, but then decided to wait for your response. Glad I did.

Given this, if Swift _were_ to allow extensions to protocols to add new dynamically dispatched methods, would there still be any reason left to provide static dispatch as an option (except as a compiler optimization, of course)?

Cheers,

Paul

···

On Dec 7, 2015, at 3:20 PM, Joe Groff <jgroff@apple.com> wrote:

On Dec 7, 2015, at 1:16 PM, Paul Cantrell <cantrell@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@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@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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Paul Cantrell) #13

The ability to use static dispatch in most cases is one of the great features of Swift, and it's something that needs to be preserved, not relegated to being an optimization that can be done occasionally.

Can you think of a situation where that’s the desired semantics, as opposed to either a desirable optimization or a confusing workaround for the “monkey collision” problem? (I can’t see the use case, but I’m open to hearing it!)

It's also worth noting that the current behavior, where methods defined in protocol extensions can't be overridden by the implementing type, may actually be the desired behavior in many cases.

If you don’t want a method to be overridable, that’s what “final” is for.

The current behavior, in which a method is silently shadowed, does not seem to be to be a desirable behavior in any situation. The compiler warnings/errors Joe Groff proposed seem like a bare minimum, but I’d prefer to eliminate the situation that needs to be warned about in the first place.

Cheers, P

···

On Dec 7, 2015, at 4:11 PM, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:

I'd love to see these two key things that Joe mentioned get added.
Diagnostics around confusing situations is a good idea (although there
needs to be a way to disable it too; what if I intentionally need to use
the same name in a type and in an extension to one of the conformed-to
protocols, for various reasons?).

I can't support the original proposal though, of making foo.bar() use
dynamic dispatch. The ability to use static dispatch in most cases is
one of the great features of Swift, and it's something that needs to be
preserved, not relegated to being an optimization that can be done
occasionally.

It's also worth noting that the current behavior, where methods defined
in protocol extensions can't be overridden by the implementing type, may
actually be the desired behavior in many cases. Heck, the ability to
trigger this different behavior is one of the three reasons to even
define methods in protocol extensions instead of in the protocol (the
others being when you're defining it for a subset of protocol
implementations, and when you're defining extensions on protocols
imported from other modules). Removing this functionality (by making
foo.bar() always dynamic) means reverting back to the Swift 1.x
convention of having global functions to provide this behavior.

I wouldn't mind seeing something like the `dynamic` keyword to allow
concrete types to override these protocol extension methods (although
`dynamic` in this case would not imply @objc, like it does today, which
may be a little confusing), but that would be opt-in behavior.

-Kevin Ballard

On Mon, Dec 7, 2015, at 12:14 PM, Joe Groff via swift-evolution 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@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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Paul Cantrell) #14

The ability to use static dispatch in most cases is one of the great features of Swift, and it's something that needs to be preserved, not relegated to being an optimization that can be done occasionally.

Can you think of a situation where that’s the desired semantics, as opposed to either a desirable optimization or a confusing workaround for the “monkey collision” problem? (I can’t see the use case, but I’m open to hearing it!)

It's also worth noting that the current behavior, where methods defined in protocol extensions can't be overridden by the implementing type, may actually be the desired behavior in many cases.

If you don’t want a method to be overridable, that’s what “final” is for.

The current behavior, in which a method is silently shadowed, does not seem to be to be a desirable behavior in any situation. The compiler warnings/errors Joe Groff proposed seem like a bare minimum, but I’d prefer to eliminate the situation that needs to be warned about in the first place.

Cheers, P

···

On Dec 7, 2015, at 4:11 PM, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:

I'd love to see these two key things that Joe mentioned get added.
Diagnostics around confusing situations is a good idea (although there
needs to be a way to disable it too; what if I intentionally need to use
the same name in a type and in an extension to one of the conformed-to
protocols, for various reasons?).

I can't support the original proposal though, of making foo.bar() use
dynamic dispatch. The ability to use static dispatch in most cases is
one of the great features of Swift, and it's something that needs to be
preserved, not relegated to being an optimization that can be done
occasionally.

It's also worth noting that the current behavior, where methods defined
in protocol extensions can't be overridden by the implementing type, may
actually be the desired behavior in many cases. Heck, the ability to
trigger this different behavior is one of the three reasons to even
define methods in protocol extensions instead of in the protocol (the
others being when you're defining it for a subset of protocol
implementations, and when you're defining extensions on protocols
imported from other modules). Removing this functionality (by making
foo.bar() always dynamic) means reverting back to the Swift 1.x
convention of having global functions to provide this behavior.

I wouldn't mind seeing something like the `dynamic` keyword to allow
concrete types to override these protocol extension methods (although
`dynamic` in this case would not imply @objc, like it does today, which
may be a little confusing), but that would be opt-in behavior.

-Kevin Ballard

On Mon, Dec 7, 2015, at 12:14 PM, Joe Groff via swift-evolution 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@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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(ilya) #15

I'd love to see these two key things that Joe mentioned get added.
Diagnostics around confusing situations is a good idea (although there
needs to be a way to disable it too; what if I intentionally need to use
the same name in a type and in an extension to one of the conformed-to
protocols, for various reasons?).

I can't support the original proposal though, of making foo.bar() use
dynamic dispatch. The ability to use static dispatch in most cases is
one of the great features of Swift, and it's something that needs to be
preserved, not relegated to being an optimization that can be done
occasionally.

It's also worth noting that the current behavior, where methods defined
in protocol extensions can't be overridden by the implementing type, may
actually be the desired behavior in many cases.

It is indeed the only logical choice; for example I could make a private
protocol extension - obviously, a class that has no access to it won't be
able to override it.

Heck, the ability to
trigger this different behavior is one of the three reasons to even
define methods in protocol extensions instead of in the protocol (the
others being when you're defining it for a subset of protocol
implementations, and when you're defining extensions on protocols
imported from other modules). Removing this functionality (by making
foo.bar() always dynamic) means reverting back to the Swift 1.x
convention of having global functions to provide this behavior.

I wouldn't mind seeing something like the `dynamic` keyword to allow
concrete types to override these protocol extension methods (although

You currently allow it by declaring method to be part of protocol, which
means that an entry for some method is reserved in the protocol witness
table. This entry will be filled either by concrete method, if present, or
by protocol default implementation.

···

On Tue, Dec 8, 2015 at 1:11 AM, Kevin Ballard via swift-evolution < swift-evolution@swift.org> wrote:

`dynamic` in this case would not imply @objc, like it does today, which
may be a little confusing), but that would be opt-in behavior.

-Kevin Ballard

On Mon, Dec 7, 2015, at 12:14 PM, Joe Groff via swift-evolution 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@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@swift.org
> > https://lists.swift.org/mailman/listinfo/swift-evolution
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Lily Ballard) #16

I wouldn't mind seeing something like the `dynamic` keyword to allow

concrete types to override these protocol extension methods (although

You currently allow it by declaring method to be part of protocol,
which means that an entry for some method is reserved in the protocol
witness table. This entry will be filled either by concrete method, if
present, or by protocol default implementation.

Sure, if you define the protocol yourself, and you want the method to be
available for all instances of the protocol. My `dynamic` suggestion is
meant to cover the cases of a) you didn't define the protocol yourself,
but you want to define a method that can be overridden by any class that
can see your protocol extension, or b) you want to define the method
only for a subset of protocol implementations (e.g. with a `where`
clause) but still want to allow for overriding.

That said, I think `dynamic` is the wrong keyword here, because it means
runtime dynamic dispatch, and what I'm really talking about is just the
ability to have a separate protocol witness table for the extension.

-Kevin Ballard

···

On Mon, Dec 7, 2015, at 02:18 PM, ilya wrote:


(Lily Ballard) #17

Yes. Any time you're replacing a global function with a protocol
extension, to preserve the semantics the protocol extension's method
implementation must be called every time the call is resolved via the
protocol (e.g. with generics or protocol objects, as opposed to calling
it on a concrete type that just happens to implement the protocol). If
you want the method implementation to be overridden by the concrete
implementing type, then the method should be declared in the protocol
instead of just in a protocol extension. Whether or not you want these
semantics is entirely up to you and your particular use-case.

I also disagree with the claim that the current rules are confusing. I
think they're very straightforward. Whenever you call a method on a
protocol value (either a generic type that's bound by the protocol, or a
protocol object), the only question that matters for method resolution
is "was this method declared in the protocol?". If it was declared in
the protocol, then concrete implementations can override it. If it was
not declared in the protocol, then concrete implementations cannot
override it. And this is a good thing. If concrete implementations could
override methods that were only provided through protocol extensions,
then that's a great way to accidentally override methods without
realizing it. Or more generally, if I declare an extension on some
protocol with some method, subjecting that method to overriding would be
a serious hazard, because there's no real way for me to know whether any
implementing type ends up accidentally overriding the method (and if
they did, they may not match the behavior expected from my method). The
only sane behavior here is to not subject it to overriding by default.

Incidentally, I rather like ilya's suggestion of declaring another
protocol with conditional conformance as the solution for declaring an
overridable method in a protocol extension. That provides a good way of
having this behavior without changing the semantics of protocols, and
all it requires is to be able to combine `where` clauses with protocol
conformance (which is something that's already known to be desired on
types, e.g. so Optional can conform to Equatable when Wrapped:
Equatable, so the only change here is extending that to protocols).

-Kevin Ballard

···

On Mon, Dec 7, 2015, at 02:51 PM, Paul Cantrell wrote:

> The ability to use static dispatch in most cases is one of the great features of Swift, and it's something that needs to be preserved, not relegated to being an optimization that can be done occasionally.

Can you think of a situation where that’s the desired semantics, as
opposed to either a desirable optimization or a confusing workaround for
the “monkey collision” problem? (I can’t see the use case, but I’m open
to hearing it!)


(Paul Cantrell) #18

Can you think of a situation where [static dispatch] is the desired semantics, as
opposed to either a desirable optimization or a confusing workaround for
the “monkey collision” problem? (I can’t see the use case, but I’m open
to hearing it!)

Yes. Any time you're replacing a global function with a protocol
extension, to preserve the semantics [etc].

Indeed, I (mistakenly?) took this to be the original rationale. It seems to me that, this case would be better served by some mechanism that makes the non-override intent explicit at the point of declaration.

I also disagree with the claim that the current rules are confusing.

In college, my brilliant mathematician friend Fazil divided all problems into two categories: “impossible” and “trivial.” The difference was whether Fazil knew the answer. Several times, I saw him tied in knots at the whiteboard, and then a problem’s status would suddenly flip in a single aha moment: “This is impossible. Impossib– Wait, no this is trivial!” [starts erasing everything]

Point is, everything seems obvious when it’s no longer confusing. I stand firm on my assertion that these rules are not obvious to those who haven’t already studied them carefully, and that a reshuffling that makes intent more explicit (and possibly allows dynamic dispatch in more circumstances) would prevent a lot of bugs out there in the wild.

If we must mix static and dynamic dispatch, I’d like to make sure every Swift programmer _chooses_ one instead of accidentally using it. I’m sure the language could do better on that front, given Joe’s comment about module-based scoping of names. I need to give this some more thought.

Cheers,

Paul

···

On Dec 7, 2015, at 5:52 PM, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:

On Mon, Dec 7, 2015, at 02:51 PM, Paul Cantrell wrote:

The ability to use static dispatch in most cases is one of the great features of Swift, and it's something that needs to be preserved, not relegated to being an optimization that can be done occasionally.

Can you think of a situation where that’s the desired semantics, as
opposed to either a desirable optimization or a confusing workaround for
the “monkey collision” problem? (I can’t see the use case, but I’m open
to hearing it!)

Yes. Any time you're replacing a global function with a protocol
extension, to preserve the semantics the protocol extension's method
implementation must be called every time the call is resolved via the
protocol (e.g. with generics or protocol objects, as opposed to calling
it on a concrete type that just happens to implement the protocol). If
you want the method implementation to be overridden by the concrete
implementing type, then the method should be declared in the protocol
instead of just in a protocol extension. Whether or not you want these
semantics is entirely up to you and your particular use-case.

I also disagree with the claim that the current rules are confusing. I
think they're very straightforward. Whenever you call a method on a
protocol value (either a generic type that's bound by the protocol, or a
protocol object), the only question that matters for method resolution
is "was this method declared in the protocol?". If it was declared in
the protocol, then concrete implementations can override it. If it was
not declared in the protocol, then concrete implementations cannot
override it. And this is a good thing. If concrete implementations could
override methods that were only provided through protocol extensions,
then that's a great way to accidentally override methods without
realizing it. Or more generally, if I declare an extension on some
protocol with some method, subjecting that method to overriding would be
a serious hazard, because there's no real way for me to know whether any
implementing type ends up accidentally overriding the method (and if
they did, they may not match the behavior expected from my method). The
only sane behavior here is to not subject it to overriding by default.

Incidentally, I rather like ilya's suggestion of declaring another
protocol with conditional conformance as the solution for declaring an
overridable method in a protocol extension. That provides a good way of
having this behavior without changing the semantics of protocols, and
all it requires is to be able to combine `where` clauses with protocol
conformance (which is something that's already known to be desired on
types, e.g. so Optional can conform to Equatable when Wrapped:
Equatable, so the only change here is extending that to protocols).

-Kevin Ballard
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(ilya) #19

I wouldn't mind seeing something like the `dynamic` keyword to allow
concrete types to override these protocol extension methods (although

You currently allow it by declaring method to be part of protocol, which
means that an entry for some method is reserved in the protocol witness
table. This entry will be filled either by concrete method, if present, or
by protocol default implementation.

Sure, if you define the protocol yourself, and you want the method to be
available for all instances of the protocol. My `dynamic` suggestion is
meant to cover the cases of a) you didn't define the protocol yourself, but
you want to define a method that can be overridden by any class that can
see your protocol extension, or b) you want to define the method only for a
subset of protocol implementations (e.g. with a `where` clause) but still
want to allow for overriding.

Interesting. I think your use case a) could work like this

protocol PP1 :P1 {
    func overrideable() -> Int
}

extension P1 :PP1 { // doesn't currently compile with :PP1 inheritance
clause
    func overrideable() -> Int {
        return 1
    }
}

extension C1 {
    func overrideable() -> Int {
        return 0
    }
}

(C1() as P1).overrideable()

···

On Tue, Dec 8, 2015 at 1:22 AM, Kevin Ballard <kevin@sb.org> wrote:

On Mon, Dec 7, 2015, at 02:18 PM, ilya wrote:

That said, I think `dynamic` is the wrong keyword here, because it means
runtime dynamic dispatch, and what I'm really talking about is just the
ability to have a separate protocol witness table for the extension.

-Kevin Ballard


(ilya) #20

> The ability to use static dispatch in most cases is one of the great
features of Swift, and it's something that needs to be preserved, not
relegated to being an optimization that can be done occasionally.

Can you think of a situation where that’s the desired semantics, as
opposed to either a desirable optimization or a confusing workaround for
the “monkey collision” problem? (I can’t see the use case, but I’m open to
hearing it!)

Sure, private protocol extension. For example,

public protocol Animal {
  func greet()
  func sit()
  func run()
}

private extension Animal {
  func compete() { greet(); sit(); run()}
}

// we are provided with animal
... animal.compete()...

···

On Tuesday, December 8, 2015, Paul Cantrell via swift-evolution < swift-evolution@swift.org> wrote:

It's also worth noting that the current behavior, where methods defined
in protocol extensions can't be overridden by the implementing type, may
actually be the desired behavior in many cases.

If you don’t want a method to be overridable, that’s what “final” is for.

The current behavior, in which a method is silently shadowed, does not
seem to be to be a desirable behavior in any situation. The compiler
warnings/errors Joe Groff proposed seem like a bare minimum, but I’d prefer
to eliminate the situation that needs to be warned about in the first place.

Cheers, P

> On Dec 7, 2015, at 4:11 PM, Kevin Ballard via swift-evolution < > swift-evolution@swift.org <javascript:;>> wrote:
>
> I'd love to see these two key things that Joe mentioned get added.
> Diagnostics around confusing situations is a good idea (although there
> needs to be a way to disable it too; what if I intentionally need to use
> the same name in a type and in an extension to one of the conformed-to
> protocols, for various reasons?).
>
> I can't support the original proposal though, of making foo.bar() use
> dynamic dispatch. The ability to use static dispatch in most cases is
> one of the great features of Swift, and it's something that needs to be
> preserved, not relegated to being an optimization that can be done
> occasionally.
>
> It's also worth noting that the current behavior, where methods defined
> in protocol extensions can't be overridden by the implementing type, may
> actually be the desired behavior in many cases. Heck, the ability to
> trigger this different behavior is one of the three reasons to even
> define methods in protocol extensions instead of in the protocol (the
> others being when you're defining it for a subset of protocol
> implementations, and when you're defining extensions on protocols
> imported from other modules). Removing this functionality (by making
> foo.bar() always dynamic) means reverting back to the Swift 1.x
> convention of having global functions to provide this behavior.
>
> I wouldn't mind seeing something like the `dynamic` keyword to allow
> concrete types to override these protocol extension methods (although
> `dynamic` in this case would not imply @objc, like it does today, which
> may be a little confusing), but that would be opt-in behavior.
>
> -Kevin Ballard
>
> On Mon, Dec 7, 2015, at 12:14 PM, Joe Groff via swift-evolution 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@swift.org <javascript:;>> 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@swift.org <javascript:;>
>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution@swift.org <javascript:;>
>> https://lists.swift.org/mailman/listinfo/swift-evolution
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org <javascript:;>
> https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <javascript:;>
https://lists.swift.org/mailman/listinfo/swift-evolution