Static Dispatch Pitfalls

Virtual/nonvirtual?

Charles

···

On May 21, 2016, at 9:40 AM, Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:

By annotation I just meant something indicating behavior. Your 'nondynamic' qualifies, but is not the right term.

'dynamic' in Swift does not just mean dynamic dispatch semantics. It means always dispatched through the Objective-C runtime. If it didn't already have this meaning I would probably like 'nondynamic' quite a bit as a more precise alternative.

I support the opinion that Swift should somehow warn us that some type has the same method(exact matched or even if the same name?) as in protocol extension for the conformed protocol(if no such method is in protocol itself *or* if declared, but conformance to protocol was introduced in base class and base contains no implemntation for method with default implementation(see my another email with example in this thread) - in both cases it will be statically dispatched)

But I don't think `final` keyword will help there as it can't help if we initially have protocol and extension in 3rd party code, and wants to conforms our(or even not our) class with *existed* methods to that protocol via type extension. So, we just can't/don't want/have no rights to change anything in protocol/extension and probably in the type.

So, the only solution I can see here -
1) warning from Swift compiler if method in type just shadowing default implementation in protocol and such protocol's mehtod will be dispatched statically(in any of two possible situation of static dispatch)
2) *plus* some posibility to 'fix' this warning. I see two options here:
some @warn_nondynamic(OurType.someMethod)
which will say to compiler "I know that OurType.someMethod is the same as protocol extension method that will be dispatched statically"
or
some extended decorations in type itself or in *type extension*:
extension OurType {
   nondynamicwarn func someMethod(); // probably ; required here
}

···

On 21.05.2016 23:16, Brent Royal-Gordon via swift-evolution wrote:

I *did* propose forcing them final, and I'm still very skeptical that a
`nondynamic` keyword that does nothing when you shadow the method is a
good idea. People are still going to naïvely try to override these
methods and be surprised when it doesn't work. A `nondynamic` keyword at
the original declaration site will help them understand what happened
when they're investigating the bug, but it still seems like this kind of
code is so suspect that Swift ought to flag it from the start.

I support *at least* to introduce such a special marker for method declared *only* in protocol extension (no declaration for it in protocol itself) to make it clear that it will be dispatched statically.
Probably right now I support `nondynamic`.

But, there is question regarding another situation when the protocol method dispatched *statically* even if *defined* in protocol declaration:

protocol A {
   func f()
   func x()
}

extension A {
   func x() {print("a-x")}
}

class B: A { // already strange. B depends on A extension. did not implement all required methods from A
   func f() {}
}

class C: B {
   func x(){print("c-x")}
}

var c : A = C()
c.x() // "a-x". but C() *implements* x() that *is* defined in protocol

IMO this is totally unexpected and must be dispatched dynamically. I believe that there can not be any explanation why this is correct: A - is a protocol, C - is a class conformed to A protocol and implemented all methods of that protocol.

It is clear, that you don't want to decorate A.x() with such `nondynamic` as if it was implemented in B - it will be dynamic:

class B: A {
   func f() {}
   func x(){print("b-x")}
}

var b : A = B()
b.x() // "b-x". now dynamic

So, again, IMO at least we need to decorate protocol extension methods that was not defined in protocol itself, but the 'issue' is bigger 'than just this.

Agreed, the issue is an important one of deterministic logical behaviour. Especially with protocols which help us decouple behaviour from implementation of said behaviour, but not just with protocols, there should not be a case in which depending on the type of the reference used you call a method or another.

If an instance implements a method and said method is called on said instance the instead implementation should execute no matter the type of the reference we are accessing it by. Message dispatching in Objective-C got this right.

I do not think always dynamic dispatching/runtime message sending is not inherently unsafe/bad... and I am willing to trade off some of the extra automagic safety for the flexibility and predictability it can bring when used well. Without multiple inheritance and default methods/code in protocols, it is easier to tame the beast and have a working design in my opinion.

···

Sent from my iPhone

On 22 May 2016, at 07:03, Vladimir.S via swift-evolution <swift-evolution@swift.org> wrote:

On 21.05.2016 16:27, Matthew Johnson via swift-evolution wrote:
Nobody is talking about forcing them final. We are talking about
annotating them with a keyword that documents their behavior (which is
unintuitive for sure but makes sense when you think through how things
work behind the scenes).

Maybe we will figure out a way to have something better in the future,
but until then highlighting the behavior via annotation is a pretty good
option.

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

I posted a small playground with a few of the identified use cases

  GitHub - lmihalkovic/swift-lang

I also wrote somewhat of a summary of the current behavior (with what might cause trouble) and a summary of a few of the proposed remedies:

  [swift-evolution] [summary] Protocol extension method dispatch - was [Static Dispatch Pitfalls]

···

On May 22, 2016, at 8:03 AM, Vladimir.S via swift-evolution <swift-evolution@swift.org> wrote:

I support *at least* to introduce such a special marker for method declared *only* in protocol extension (no declaration for it in protocol itself) to make it clear that it will be dispatched statically.
Probably right now I support `nondynamic`.

But, there is question regarding another situation when the protocol method dispatched *statically* even if *defined* in protocol declaration:

protocol A {
   func f()
   func x()
}

extension A {
   func x() {print("a-x")}
}

class B: A { // already strange. B depends on A extension. did not implement all required methods from A
   func f() {}
}

class C: B {
   func x(){print("c-x")}
}

var c : A = C()
c.x() // "a-x". but C() *implements* x() that *is* defined in protocol

IMO this is totally unexpected and must be dispatched dynamically. I believe that there can not be any explanation why this is correct: A - is a protocol, C - is a class conformed to A protocol and implemented all methods of that protocol.

It is clear, that you don't want to decorate A.x() with such `nondynamic` as if it was implemented in B - it will be dynamic:

class B: A {
   func f() {}
   func x(){print("b-x")}
}

var b : A = B()
b.x() // "b-x". now dynamic

So, again, IMO at least we need to decorate protocol extension methods that was not defined in protocol itself, but the 'issue' is bigger 'than just this.

On 21.05.2016 16:27, Matthew Johnson via swift-evolution wrote:

Nobody is talking about forcing them final. We are talking about
annotating them with a keyword that documents their behavior (which is
unintuitive for sure but makes sense when you think through how things
work behind the scenes).

Maybe we will figure out a way to have something better in the future,
but until then highlighting the behavior via annotation is a pretty good
option.

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

Making them 'final' causes problems with retroactive modeling.

Because of that issue, I had an attribute you could use to acknowledge conflicts and disable the error. But this ended up being rather complex: you needed to be able to label a member *or* an extension *or* an import which made a conflicting member and conformance visible in the same place.

Here's the last draft of the proposal, from way back in December: <https://github.com/brentdax/swift-evolution/blob/final-protocol-methods/proposals/0000-require-final-on-protocol-extension-methods.md&gt;

···

--
Brent Royal-Gordon
Architechies

indeed. IMHO a proper solution should deal with these use cases properly:

1) Someone adds a method to a class, and there are protocol extensions somewhere out there (maybe in another module) that he doesn't know about.
2) Someone creates a protocol extension for a type X, but he doesn't know the names of all (current and future) methods from all subtypes of X. How can this poor developer ever create a safe protocol extension?

This should not lead to
A) the class breaking (e.g. stopping to compile), because someone else creates a protocol extension with a conflicting symbol.
B) the wrong method being dispatched, because the two authors didn't agree on what the "makeBlue" method should do. they disagree, because they don't know each other. This becomes especially bad if both methods are not well-documented, and the contract mismatch is not obvious.

Can we agree that two methods with the same name sometimes have the same contract and sometimes not? And that this is not a programmer error? And that it would be good to distinguish between these two cases?

The more I think about it and follow the discussion, the more I come to the conclusion that the current behavior is not that bad. At least it is safe. Dispatching everything dynamically is unsafe (1). Adding a `nondynamic` keyword is unsafe (2). Forcing a `final` keyword is unsafe (3).

(1) I wrote about that earlier (yesterday I think)
(2) Because you would usually just rename such a method. It's much better. The problem becomes apparent when the class author doesn't know the protocol extension, or when the protocol extension is created later, maybe by a different author in a different module.
(3) This disallows methods with the same name in classes that fall into the scope of the extension. See (2).

-Michael

···

Am 21.05.2016 um 23:22 schrieb Matthew Johnson via swift-evolution <swift-evolution@swift.org>:

Sent from my iPad

On May 21, 2016, at 3:16 PM, Brent Royal-Gordon <brent@architechies.com> wrote:

Where was proposed to force them to be final and was just trying to provide an example in which the conforming class may want to override them, so forcing them final isn't a good idea.

Again, nobody ever proposed forcing them final. The proposal was to use final as a way to document the existing behavior.

I *did* propose forcing them final, and I'm still very skeptical that a `nondynamic` keyword that does nothing when you shadow the method is a good idea. People are still going to naïvely try to override these methods and be surprised when it doesn't work. A `nondynamic` keyword at the original declaration site will help them understand what happened when they're investigating the bug, but it still seems like this kind of code is so suspect that Swift ought to flag it from the start.

Sorry about that. Either I didn't see that or I forgot about it. Making them 'final' causes problems with retroactive modeling. There are other issues as well but that is the most significant one.

Making them 'final' causes problems with retroactive modeling.

Because of that issue, I had an attribute you could use to acknowledge conflicts and disable the error. But this ended up being rather complex: you needed to be able to label a member *or* an extension *or* an import which made a conflicting member and conformance visible in the same place.

Read the proposal... I have an aversion to-go coffee cups that remind people that hot coffee may burn them, and when my daughter was 4 we explained to her why knives were to be handled with care, rather than remove them all from her sight. IMHO the proposal evoques mandating training wheels rather than letting people learn naturally from their errors.

···

On May 21, 2016, at 11:50 PM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

Here's the last draft of the proposal, from way back in December: <https://github.com/brentdax/swift-evolution/blob/final-protocol-methods/proposals/0000-require-final-on-protocol-extension-methods.md&gt;

--
Brent Royal-Gordon
Architechies

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

Making them 'final' causes problems with retroactive modeling.

Because of that issue, I had an attribute you could use to acknowledge conflicts and disable the error. But this ended up being rather complex: you needed to be able to label a member *or* an extension *or* an import which made a conflicting member and conformance visible in the same place.

Here's the last draft of the proposal, from way back in December: <https://github.com/brentdax/swift-evolution/blob/final-protocol-methods/proposals/0000-require-final-on-protocol-extension-methods.md&gt;

Now I remember this thread. Swift evolution was so active at the time that I think I didn't follow it too closely.

The proposal is well thought out and makes a valiant attempt at handling all of the issues necessary. But I don't support it for a number of reasons. I think it highlights how awkward it would be to try to address shadowing on a case-by-case basis, which isn't necessarily obvious until you explore what a solution might look like. (And btw, 'final' in this proposal is not exactly, because when combined with @incoherent the methods are not actually 'final' - there is a necessary escape hatch).

There are two things we should do to help reduce confusion around this. First, we should allow default implementations in the protocol declaration itself (I believe this is intended and is considered an implement ion limitation right now).

Second, we should require annotation of methods in protocol extensions that are not default implementation of requirements. Maybe 'shadowable' or 'staticdispatch'? These are awkward, but so is the behavior and they describe it better than anything else I've seen so far (maybe we can do better though).

I don't like 'nondynamic' both because it is not aligned with the meaning of 'dynamic' and also because it only says what the behavior *is not* rather than what the behavior *is*.

-Matthew

···

Sent from my iPad
On May 21, 2016, at 4:50 PM, Brent Royal-Gordon <brent@architechies.com> wrote:

--
Brent Royal-Gordon
Architechies

I can partially support this opinion. But we have a situation with protocol extension methods and static dispatches in which we need Swift's help on compilation stage. IMO Using your words, right now we just got knife in our hands *without* any explanation. Then we hurt ourselves, and *then* we know that such methods will be dispatched statically(and the rule of dispatch is quite non-obvious). This is another extreme like "remove all knives". We need some golden middle. Personally I believe the solution is in compiler warning and in some method to 'fix' this warning.

···

On 22.05.2016 3:01, L. Mihalkovic via swift-evolution wrote:

Read the proposal... I have an aversion to-go coffee cups that remind
people that hot coffee may burn them, and when my daughter was 4 we
explained to her why knives were to be handled with care, rather than
remove them all from her sight. IMHO the proposal evoques mandating
training wheels rather than letting people learn naturally from their
errors.

The proposal is well thought out and makes a valiant attempt at handling all of the issues necessary. But I don't support it for a number of reasons. I think it highlights how awkward it would be to try to address shadowing on a case-by-case basis, which isn't necessarily obvious until you explore what a solution might look like.

It does, but I'm just not sure what else you can do about it. If there's a warning, you need a way to silence it. If you ignore some cases (like creating a conflict by importing two modules), you'll miss some of the subtlest and hardest-to-fix bugs.

Honestly, I'm tempted to say "you just can't ever shadow a final protocol method" and be done with it. If that prevents certain conformances or stops certain imports, so be it. You can always work around that with wrapper types or other techniques.

(And btw, 'final' in this proposal is not exactly, because when combined with @incoherent the methods are not actually 'final' - there is a necessary escape hatch).

There is no particular reason you couldn't allow similar annotated shadowing of `final` methods on classes; they would have basically the same semantics as you get here, where if a piece of code knows it's working with the subclass you get subclass semantics, but otherwise you get superclass ones. I do not claim this is a good idea. :^)

Second, we should require annotation of methods in protocol extensions that are not default implementation of requirements. Maybe 'shadowable' or 'staticdispatch'? These are awkward, but so is the behavior and they describe it better than anything else I've seen so far (maybe we can do better though).

I don't think `shadowable` makes sense here; that doesn't acknowledge a limitation, which is what we're trying to do here.

I continue to wish we hadn't taken `static` for statically-dispatched type methods. But I lost that argument years before swift-evolution became a thing.

I don't like 'nondynamic' both because it is not aligned with the meaning of 'dynamic' and also because it only says what the behavior *is not* rather than what the behavior *is*.

I do understand what you mean here. Unfortunately, the word "virtual" in a keyword makes me break out in hives, and I'm not sure what else we might base it on.

This is why I selected `final` in my proposal. `final` is desperately close to the actual semantic here, far closer than anything else in the language.

···

--
Brent Royal-Gordon
Architechies

Read the proposal... I have an aversion to-go coffee cups that remind
people that hot coffee may burn them, and when my daughter was 4 we
explained to her why knives were to be handled with care, rather than
remove them all from her sight. IMHO the proposal evoques mandating
training wheels rather than letting people learn naturally from their
errors.

I can partially support this opinion. But we have a situation with protocol extension methods and static dispatches in which we need Swift's help on compilation stage. IMO Using your words, right now we just got knife in our hands *without* any explanation. Then we hurt ourselves, and *then* we know that such methods will be dispatched statically(and the rule of dispatch is quite non-obvious). This is another extreme like "remove all knives". We need some golden middle. Personally I believe the solution is in compiler warning and in some method to 'fix' this warning.

Why not just make it an error and require an annotation on the extension methods?

···

Sent from my iPad

On May 22, 2016, at 1:49 AM, Vladimir.S via swift-evolution <swift-evolution@swift.org> wrote:

On 22.05.2016 3:01, L. Mihalkovic via swift-evolution wrote:

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

In other words, we should declare knives as UnsafePointy things ;-)

···

Am 22.05.2016 um 08:49 schrieb Vladimir.S via swift-evolution <swift-evolution@swift.org>:

On 22.05.2016 3:01, L. Mihalkovic via swift-evolution wrote:
Read the proposal... I have an aversion to-go coffee cups that remind
people that hot coffee may burn them, and when my daughter was 4 we
explained to her why knives were to be handled with care, rather than
remove them all from her sight. IMHO the proposal evoques mandating
training wheels rather than letting people learn naturally from their
errors.

I can partially support this opinion. But we have a situation with protocol extension methods and static dispatches in which we need Swift's help on compilation stage. IMO Using your words, right now we just got knife in our hands *without* any explanation. Then we hurt ourselves, and *then* we know that such methods will be dispatched statically(and the rule of dispatch is quite non-obvious). This is another extreme like "remove all knives". We need some golden middle. Personally I believe the solution is in compiler warning and in some method to 'fix' this warning.

Sent from my iPad

Read the proposal... I have an aversion to-go coffee cups that remind
people that hot coffee may burn them, and when my daughter was 4 we
explained to her why knives were to be handled with care, rather than
remove them all from her sight. IMHO the proposal evoques mandating
training wheels rather than letting people learn naturally from their
errors.

I can partially support this opinion. But we have a situation with protocol extension methods and static dispatches in which we need Swift's help on compilation stage. IMO Using your words, right now we just got knife in our hands *without* any explanation. Then we hurt ourselves, and *then* we know that such methods will be dispatched statically(and the rule of dispatch is quite non-obvious). This is another extreme like "remove all knives". We need some golden middle. Personally I believe the solution is in compiler warning and in some method to 'fix' this warning.

Why not just make it an error and require an annotation on the extension methods?

See [swift-evolution] [summary] Protocol extension method dispatch - was [Static Dispatch Pitfalls]
And https://github.com/lmihalkovic/swift-lang/tree/master/Dispatching.playground

···

On May 22, 2016, at 3:15 PM, Matthew Johnson <matthew@anandabits.com> wrote:

On May 22, 2016, at 1:49 AM, Vladimir.S via swift-evolution <swift-evolution@swift.org> wrote:
On 22.05.2016 3:01, L. Mihalkovic via swift-evolution wrote:

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

> The proposal is well thought out and makes a valiant attempt at handling
all of the issues necessary. But I don't support it for a number of
reasons. I think it highlights how awkward it would be to try to address
shadowing on a case-by-case basis, which isn't necessarily obvious until
you explore what a solution might look like.

It does, but I'm just not sure what else you can do about it. If there's a
warning, you need a way to silence it. If you ignore some cases (like
creating a conflict by importing two modules), you'll miss some of the
subtlest and hardest-to-fix bugs.

Honestly, I'm tempted to say "you just can't ever shadow a final protocol
method" and be done with it. If that prevents certain conformances or stops
certain imports, so be it. You can always work around that with wrapper
types or other techniques.

You know, I think this might be cleverest solution. It adds a small limit
to the language, but it doesn't unduly penalize retroactive modeling. If
you control either the protocol or the conforming type, you can change the
name of one of the methods so it doesn't shadow/get shadowed by the other.

> (And btw, 'final' in this proposal is not exactly, because when combined
with @incoherent the methods are not actually 'final' - there is a
necessary escape hatch).

There is no particular reason you couldn't allow similar annotated
shadowing of `final` methods on classes; they would have basically the same
semantics as you get here, where if a piece of code knows it's working with
the subclass you get subclass semantics, but otherwise you get superclass
ones. I do not claim this is a good idea. :^)

> Second, we should require annotation of methods in protocol extensions
that are not default implementation of requirements. Maybe 'shadowable' or
'staticdispatch'? These are awkward, but so is the behavior and they
describe it better than anything else I've seen so far (maybe we can do
better though).

I don't think `shadowable` makes sense here; that doesn't acknowledge a
limitation, which is what we're trying to do here.

I continue to wish we hadn't taken `static` for statically-dispatched type
methods. But I lost that argument years before swift-evolution became a
thing.

> I don't like 'nondynamic' both because it is not aligned with the
meaning of 'dynamic' and also because it only says what the behavior *is
not* rather than what the behavior *is*.

I do understand what you mean here. Unfortunately, the word "virtual" in a
keyword makes me break out in hives, and I'm not sure what else we might
base it on.

This is why I selected `final` in my proposal. `final` is desperately
close to the actual semantic here, far closer than anything else in the
language.

How about `nonoverridable`? That said, I agree with earlier comments that
training-wheel annotations probably aren't the way to go. Maybe, as you
suggest above, just don't allow shadowing at all.

···

On Sun, May 22, 2016 at 3:38 PM, Brent Royal-Gordon via swift-evolution < swift-evolution@swift.org> wrote:

--
Brent Royal-Gordon
Architechies

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

The proposal is well thought out and makes a valiant attempt at handling all of the issues necessary. But I don't support it for a number of reasons. I think it highlights how awkward it would be to try to address shadowing on a case-by-case basis, which isn't necessarily obvious until you explore what a solution might look like.

It does, but I'm just not sure what else you can do about it. If there's a warning, you need a way to silence it. If you ignore some cases (like creating a conflict by importing two modules), you'll miss some of the subtlest and hardest-to-fix bugs.

Honestly, I'm tempted to say "you just can't ever shadow a final protocol method" and be done with it. If that prevents certain conformances or stops certain imports, so be it. You can always work around that with wrapper types or other techniques.

That’s pretty extreme.

I’m curious - does this still bite you now that you understand the behavior? Or are you mostly concerned about users who don’t really understand how the language works?

(And btw, 'final' in this proposal is not exactly, because when combined with @incoherent the methods are not actually 'final' - there is a necessary escape hatch).

There is no particular reason you couldn't allow similar annotated shadowing of `final` methods on classes; they would have basically the same semantics as you get here, where if a piece of code knows it's working with the subclass you get subclass semantics, but otherwise you get superclass ones. I do not claim this is a good idea. :^)

Lol. That would be terrible! There are very good implementation reasons for the behavior of protocol extension methods. There would be no such justification with classes. IMO `final` should mean final, period.

Second, we should require annotation of methods in protocol extensions that are not default implementation of requirements. Maybe 'shadowable' or 'staticdispatch'? These are awkward, but so is the behavior and they describe it better than anything else I've seen so far (maybe we can do better though).

I don't think `shadowable` makes sense here; that doesn't acknowledge a limitation, which is what we're trying to do here.

Sure, I admit it’s not great. This is a pretty tricky naming problem!

I continue to wish we hadn't taken `static` for statically-dispatched type methods. But I lost that argument years before swift-evolution became a thing.

I don't like 'nondynamic' both because it is not aligned with the meaning of 'dynamic' and also because it only says what the behavior *is not* rather than what the behavior *is*.

I do understand what you mean here. Unfortunately, the word "virtual" in a keyword makes me break out in hives, and I'm not sure what else we might base it on.

Yeah, I don’t think `virtual` has a place in Swift.

This is why I selected `final` in my proposal. `final` is desperately close to the actual semantic here, far closer than anything else in the language.

I agree `final` is kind of close, but not close enough. It has a very precise meaning and I prefer to keep it that way.

···

On May 22, 2016, at 3:38 PM, Brent Royal-Gordon <brent@architechies.com> wrote:

--
Brent Royal-Gordon
Architechies

Sent from my iPad

Read the proposal... I have an aversion to-go coffee cups that remind
people that hot coffee may burn them, and when my daughter was 4 we
explained to her why knives were to be handled with care, rather than
remove them all from her sight. IMHO the proposal evoques mandating
training wheels rather than letting people learn naturally from their
errors.

I can partially support this opinion. But we have a situation with protocol extension methods and static dispatches in which we need Swift's help on compilation stage. IMO Using your words, right now we just got knife in our hands *without* any explanation. Then we hurt ourselves, and *then* we know that such methods will be dispatched statically(and the rule of dispatch is quite non-obvious). This is another extreme like "remove all knives". We need some golden middle. Personally I believe the solution is in compiler warning and in some method to 'fix' this warning.

Why not just make it an error and require an annotation on the extension methods?

See [swift-evolution] [summary] Protocol extension method dispatch - was [Static Dispatch Pitfalls]
And https://github.com/lmihalkovic/swift-lang/tree/master/Dispatching.playground

That doesn't answer my question. I don't like any of the suggestions you posted. I think we should just leave the behavior as is (at least for now) and just require annotations on non-default methods in protocol extensions (making it an error to declare a non-default method without the annotation).

···

Sent from my iPad

On May 22, 2016, at 8:39 AM, L. Mihalkovic <laurent.mihalkovic@gmail.com> wrote:

On May 22, 2016, at 3:15 PM, Matthew Johnson <matthew@anandabits.com> wrote:

On May 22, 2016, at 1:49 AM, Vladimir.S via swift-evolution <swift-evolution@swift.org> wrote:
On 22.05.2016 3:01, L. Mihalkovic via swift-evolution wrote:

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

This is an interesting idea. Here's another (I'm just spitballing here):

Extension methods without a requirement must be marked with a @nonrequired attribute (names are all provisional).

If a requirement is later added to a protocol with the name of a previously declared nonrequired method, an extension must also be declared marking those method names @required:

protocol Foo { }
extension Foo {
  @nonrequired func blah(x: Int) {
     print(x + 100)
  }
}

Later:

protocol FooChild : Foo {
  func blah(x: Int)
}

// requires to compile
extension FooChild {
  @required blah(x:)
}

The only point of this would be to allow it to be made clear to users whether that protocol method is a requirement or not after extensions and retroactive modeling have occurred, and thus help them reason about whether it would be statically or dynamically dispatched.

Austin

···

Sent from my iPhone

On May 22, 2016, at 12:53 PM, Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:

On May 22, 2016, at 12:36 PM, L. Mihalkovic <laurent.mihalkovic@gmail.com> wrote:

On May 22, 2016, at 5:31 PM, Matthew Johnson <matthew@anandabits.com> wrote:

Sent from my iPad

On May 22, 2016, at 8:39 AM, L. Mihalkovic <laurent.mihalkovic@gmail.com> wrote:

On May 22, 2016, at 3:15 PM, Matthew Johnson <matthew@anandabits.com> wrote:

Sent from my iPad

On May 22, 2016, at 1:49 AM, Vladimir.S via swift-evolution <swift-evolution@swift.org> wrote:

On 22.05.2016 3:01, L. Mihalkovic via swift-evolution wrote:
Read the proposal... I have an aversion to-go coffee cups that remind
people that hot coffee may burn them, and when my daughter was 4 we
explained to her why knives were to be handled with care, rather than
remove them all from her sight. IMHO the proposal evoques mandating
training wheels rather than letting people learn naturally from their
errors.

I can partially support this opinion. But we have a situation with protocol extension methods and static dispatches in which we need Swift's help on compilation stage. IMO Using your words, right now we just got knife in our hands *without* any explanation. Then we hurt ourselves, and *then* we know that such methods will be dispatched statically(and the rule of dispatch is quite non-obvious). This is another extreme like "remove all knives". We need some golden middle. Personally I believe the solution is in compiler warning and in some method to 'fix' this warning.

Why not just make it an error and require an annotation on the extension methods?

See [swift-evolution] [summary] Protocol extension method dispatch - was [Static Dispatch Pitfalls]
And https://github.com/lmihalkovic/swift-lang/tree/master/Dispatching.playground

That doesn't answer my question. I don't like any of the suggestions you posted. I think we should just leave the behavior as is (at least for now) and just require annotations on non-default methods in protocol extensions (making it an error to declare a non-default method without the annotation).

Considering how things work fine the way they are today once THEIR logic is understood (the only problem today seems to be one of expectations mismatch, not that something is broken or illogic), it would IMHO be a waste of energy to revisit only to end up with a system that would only support a single model. It something is done, it might as well be to add freedom.

So if you make it optional to have an attribute (I seem to recall chris explicitely saying that annotations were not the idiomatic swift way to convey these types of behavirial adjustments) on non required extension methods, then what you describe is basically one of the proposals I listed..

It would be pointless for this to be optional. The whole point is to have the code reflect the fact that the dispatch semantics are likely to be surprising until you learn the rules (and also if you forget to consider them), and to always keep this fact in our minds as we work with protocol extensions.

Which of your proposals do you think this matches?

1. `override` in implementing classes.
2. `straw_man_dynamic_dispatch` at call site.
3. `straw_man_dynamic_dispatch` at declaration site in protocol extension.
4. doesn’t make sense (we are never going to statically dispatch protocol requirements and we can’t dynamically non-required extension methods in Swift 3)
5. `straw_man_default_attribute` on default methods in protocol extensions.

None of these are what I suggest. What I suggest is that we leave behavior alone and we *don’t* annotate default implementations because those don’t surprise anyone. I suggest requiring this:

`straw_man_static_dispatch` at declaration site of non-default methods in protocol extensions.

The only question is what actual modifier we use (`final` and `nondynamic` are obvious candidates, but both are non-starters in my mind for reasons I have stated earlier).

I do think it would be a good idea to introduce this in Swift 3 as it is a breaking change.

for a simple reason mind you... what I listed is what I saw people debate. A few options were left out as being too complex to summarize, or not ringing true to what exists today and what has been explained about protocols with great clarity by Dave on stage at WWDC. Interestingly enough, the idea of default methods on the protocol itself (which I like in java8) was already listed by him and at least someone else in the compiler team.

Coming from working in asm, c, c++, perl, tcl, vb, java, c#, js, objc, scala, typescript and go in that order, I can get used to anything as long as its logic seems reasonable and adequately explained. After reading so many emails I thought I might not be the only one craving a simple one page summary of what had been said so far.

_______________________________________________
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

Sent from my iPad

Sent from my iPad

Read the proposal... I have an aversion to-go coffee cups that remind
people that hot coffee may burn them, and when my daughter was 4 we
explained to her why knives were to be handled with care, rather than
remove them all from her sight. IMHO the proposal evoques mandating
training wheels rather than letting people learn naturally from their
errors.

I can partially support this opinion. But we have a situation with protocol extension methods and static dispatches in which we need Swift's help on compilation stage. IMO Using your words, right now we just got knife in our hands *without* any explanation. Then we hurt ourselves, and *then* we know that such methods will be dispatched statically(and the rule of dispatch is quite non-obvious). This is another extreme like "remove all knives". We need some golden middle. Personally I believe the solution is in compiler warning and in some method to 'fix' this warning.

Why not just make it an error and require an annotation on the extension methods?

See [swift-evolution] [summary] Protocol extension method dispatch - was [Static Dispatch Pitfalls]
And https://github.com/lmihalkovic/swift-lang/tree/master/Dispatching.playground

That doesn't answer my question. I don't like any of the suggestions you posted. I think we should just leave the behavior as is (at least for now) and just require annotations on non-default methods in protocol extensions (making it an error to declare a non-default method without the annotation).

Considering how things work fine the way they are today once THEIR logic is understood (the only problem today seems to be one of expectations mismatch, not that something is broken or illogic), it would IMHO be a waste of energy to revisit only to end up with a system that would only support a single model. It something is done, it might as well be to add freedom.

So if you make it optional to have an attribute (I seem to recall chris explicitely saying that annotations were not the idiomatic swift way to convey these types of behavirial adjustments) on non required extension methods, then what you describe is basically one of the proposals I listed.. for a simple reason mind you... what I listed is what I saw people debate. A few options were left out as being too complex to summarize, or not ringing true to what exists today and what has been explained about protocols with great clarity by Dave on stage at WWDC. Interestingly enough, the idea of default methods on the protocol itself (which I like in java8) was already listed by him and at least someone else in the compiler team.

Coming from working in asm, c, c++, perl, tcl, vb, java, c#, js, objc, scala, typescript and go in that order, I can get used to anything as long as its logic seems reasonable and adequately explained. After reading so many emails I thought I might not be the only one craving a simple one page summary of what had been said so far.

···

On May 22, 2016, at 5:31 PM, Matthew Johnson <matthew@anandabits.com> wrote:

On May 22, 2016, at 8:39 AM, L. Mihalkovic <laurent.mihalkovic@gmail.com> wrote:

On May 22, 2016, at 3:15 PM, Matthew Johnson <matthew@anandabits.com> wrote:

On May 22, 2016, at 1:49 AM, Vladimir.S via swift-evolution <swift-evolution@swift.org> wrote:
On 22.05.2016 3:01, L. Mihalkovic via swift-evolution wrote:

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

> The proposal is well thought out and makes a valiant attempt at handling all of the issues necessary. But I don't support it for a number of reasons. I think it highlights how awkward it would be to try to address shadowing on a case-by-case basis, which isn't necessarily obvious until you explore what a solution might look like.

It does, but I'm just not sure what else you can do about it. If there's a warning, you need a way to silence it. If you ignore some cases (like creating a conflict by importing two modules), you'll miss some of the subtlest and hardest-to-fix bugs.

Honestly, I'm tempted to say "you just can't ever shadow a final protocol method" and be done with it. If that prevents certain conformances or stops certain imports, so be it. You can always work around that with wrapper types or other techniques.

You know, I think this might be cleverest solution. It adds a small limit to the language, but it doesn't unduly penalize retroactive modeling. If you control either the protocol or the conforming type, you can change the name of one of the methods so it doesn't shadow/get shadowed by the other.

If you control the conforming type this isn’t too big an issue as long as the protocol was well designed. However, if the protocol was poorly designed it could be an issue. Maybe a method that can be more efficiently implemented by some types was not made a requirement, but an extension method (with a slower implementation) takes the obvious name. Maybe you would be willing to live with the slower implementation when your type is accessed via the protocol, because at least it can still be used via the protocol, but you don’t want to burden callers who use the concrete type with the slow implementation. What do you do then?

If you control the protocol but want to retroactively model types you do not control this assumes you are willing to design your protocol around those types. What if one of those types happens to implement a method that should not be a requirement of the protocol for one reason or another, but will be implemented as an extension method. What do you do then?

And of course there are cases where you do not control either. Some people write code with a lot of 3rd party dependencies these days (not my style, but pretty common). This is not a trivial concern.

> (And btw, 'final' in this proposal is not exactly, because when combined with @incoherent the methods are not actually 'final' - there is a necessary escape hatch).

There is no particular reason you couldn't allow similar annotated shadowing of `final` methods on classes; they would have basically the same semantics as you get here, where if a piece of code knows it's working with the subclass you get subclass semantics, but otherwise you get superclass ones. I do not claim this is a good idea. :^)

> Second, we should require annotation of methods in protocol extensions that are not default implementation of requirements. Maybe 'shadowable' or 'staticdispatch'? These are awkward, but so is the behavior and they describe it better than anything else I've seen so far (maybe we can do better though).

I don't think `shadowable` makes sense here; that doesn't acknowledge a limitation, which is what we're trying to do here.

I continue to wish we hadn't taken `static` for statically-dispatched type methods. But I lost that argument years before swift-evolution became a thing.

> I don't like 'nondynamic' both because it is not aligned with the meaning of 'dynamic' and also because it only says what the behavior *is not* rather than what the behavior *is*.

I do understand what you mean here. Unfortunately, the word "virtual" in a keyword makes me break out in hives, and I'm not sure what else we might base it on.

This is why I selected `final` in my proposal. `final` is desperately close to the actual semantic here, far closer than anything else in the language.

How about `nonoverridable`? That said, I agree with earlier comments that training-wheel annotations probably aren't the way to go. Maybe, as you suggest above, just don't allow shadowing at all.

Unfortunately, ‘nonoverridable’ doesn’t really make sense because you don’t ‘override’ protocol requirements.

···

On May 22, 2016, at 4:22 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Sun, May 22, 2016 at 3:38 PM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

--
Brent Royal-Gordon
Architechies

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

Sent from my iPad

Sent from my iPad

Read the proposal... I have an aversion to-go coffee cups that remind
people that hot coffee may burn them, and when my daughter was 4 we
explained to her why knives were to be handled with care, rather than
remove them all from her sight. IMHO the proposal evoques mandating
training wheels rather than letting people learn naturally from their
errors.

I can partially support this opinion. But we have a situation with protocol extension methods and static dispatches in which we need Swift's help on compilation stage. IMO Using your words, right now we just got knife in our hands *without* any explanation. Then we hurt ourselves, and *then* we know that such methods will be dispatched statically(and the rule of dispatch is quite non-obvious). This is another extreme like "remove all knives". We need some golden middle. Personally I believe the solution is in compiler warning and in some method to 'fix' this warning.

Why not just make it an error and require an annotation on the extension methods?

See [swift-evolution] [summary] Protocol extension method dispatch - was [Static Dispatch Pitfalls]
And https://github.com/lmihalkovic/swift-lang/tree/master/Dispatching.playground

That doesn't answer my question. I don't like any of the suggestions you posted. I think we should just leave the behavior as is (at least for now) and just require annotations on non-default methods in protocol extensions (making it an error to declare a non-default method without the annotation).

Considering how things work fine the way they are today once THEIR logic is understood (the only problem today seems to be one of expectations mismatch, not that something is broken or illogic), it would IMHO be a waste of energy to revisit only to end up with a system that would only support a single model. It something is done, it might as well be to add freedom.

So if you make it optional to have an attribute (I seem to recall chris explicitely saying that annotations were not the idiomatic swift way to convey these types of behavirial adjustments) on non required extension methods, then what you describe is basically one of the proposals I listed..

It would be pointless for this to be optional. The whole point is to have the code reflect the fact that the dispatch semantics are likely to be surprising until you learn the rules (and also if you forget to consider them), and to always keep this fact in our minds as we work with protocol extensions.

Which of your proposals do you think this matches?

1. `override` in implementing classes.
2. `straw_man_dynamic_dispatch` at call site.
3. `straw_man_dynamic_dispatch` at declaration site in protocol extension.
4. doesn’t make sense (we are never going to statically dispatch protocol requirements and we can’t dynamically non-required extension methods in Swift 3)
5. `straw_man_default_attribute` on default methods in protocol extensions.

None of these are what I suggest. What I suggest is that we leave behavior alone and we *don’t* annotate default implementations because those don’t surprise anyone. I suggest requiring this:

`straw_man_static_dispatch` at declaration site of non-default methods in protocol extensions.

It truly was not obvious that this is a subset of what 3 is allowing? I debated adding something to the effect of "the attribute is NOT restricted to any particular type of method and can be applied to conformance providing method and/or additional-so-called helper methods", but thought it was self evident.
Although it might be construed as surprising-on-first-encounter, it is statistically impossible that I would be the only person outside the core team who would find the current situation justifiable. I think the POP model is somewhat surprising when considered from a objc or even java viewpoint, but it does make sense when considered by itself, based on the explanations from last year's (or before) WWDC.

The only question is what actual modifier we use (`final` and `nondynamic` are obvious candidates, but both are non-starters in my mind for reasons I have stated earlier).

I do think it would be a good idea to introduce this in Swift 3 as it is a breaking change.

-1 for the lack of elegance.

···

On May 22, 2016, at 9:53 PM, Matthew Johnson <matthew@anandabits.com> wrote:

On May 22, 2016, at 12:36 PM, L. Mihalkovic <laurent.mihalkovic@gmail.com> wrote:
On May 22, 2016, at 5:31 PM, Matthew Johnson <matthew@anandabits.com> wrote:

On May 22, 2016, at 8:39 AM, L. Mihalkovic <laurent.mihalkovic@gmail.com> wrote:
On May 22, 2016, at 3:15 PM, Matthew Johnson <matthew@anandabits.com> wrote:

On May 22, 2016, at 1:49 AM, Vladimir.S via swift-evolution <swift-evolution@swift.org> wrote:
On 22.05.2016 3:01, L. Mihalkovic via swift-evolution wrote:

for a simple reason mind you... what I listed is what I saw people debate. A few options were left out as being too complex to summarize, or not ringing true to what exists today and what has been explained about protocols with great clarity by Dave on stage at WWDC. Interestingly enough, the idea of default methods on the protocol itself (which I like in java8) was already listed by him and at least someone else in the compiler team.

Coming from working in asm, c, c++, perl, tcl, vb, java, c#, js, objc, scala, typescript and go in that order, I can get used to anything as long as its logic seems reasonable and adequately explained. After reading so many emails I thought I might not be the only one craving a simple one page summary of what had been said so far.

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