Static Dispatch Pitfalls

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.

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.

···

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

It is logical because by definition it is not a random behaviour and it works as it is not marred by bugs and yes you can make it work (variable amount of pain depending how big he code base is and how many people work on it), but the problem of expectations mismatch and a bit steep learning curve (and probability of submarine errors popping up to haunt future self) is not one that should be swept under the rug.

Part of the problem comes from the desire to put code in protocols/interfaces. Java went full steam ahead with it in Java 8 and Swift did so later on, but I still feel it creates quite a bit of problems, maybe more than it solves. Sure, it is giving life to a new magical paradigm, but all magic comes with a price ;). I may be too defensive about this change, but I would love to read a very very convincing arguments as to why I would want anything more than methods (or properties maybe, but they are methods too ;)) in a protocol/interface. I thought the point was that behaviour and implementation ought to be decoupled :).

One could bends things a bit and say that following a relatively strict TDD approach with genuine unit tests (everything is a mock but the class under test and Apple's own classes perhaps), integration, and automation tests that Objective-C's opt-in strictness (hello -Weverything, lightweight generics, nullability annotations, and various annotations like instancetype, NS_REQUIRES_SUPER, NS_DESIGNATED_INITIALIZER, etc...) is more than a safe enough path that gives you the most freedom and runtime responsibility to be used wisely and productively. Cocoa is a wonderful API to write apps with and yes it is not very Swift like and linked to the Objective-C runtime also in style, but perhaps that says something NCOs about both languages. In Objective-C having message sending/dynamic polymorphism by default never introduced illogical or hard to work through behaviour.

···

Sent from my iPhone

On 22 May 2016, at 18:36, L. Mihalkovic via swift-evolution <swift-evolution@swift.org> 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:

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.

_______________________________________________
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

Regards
(From mobile)

Sent from my iPhone

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.

It is logical because by definition it is not a random behaviour and it works as it is not marred by bugs and yes you can make it work (variable amount of pain depending how big he code base is and how many people work on it), but the problem of expectations mismatch and a bit steep learning curve (and probability of submarine errors popping up to haunt future self) is not one that should be swept under the rug.

Part of the problem comes from the desire to put code in protocols/interfaces. Java went full steam ahead with it in Java 8 and Swift did so later on, but I still feel it creates quite a bit of problems, maybe more than it solves. Sure, it is giving life to a new magical paradigm, but all magic comes with a price ;). I may be too defensive about this change, but I would love to read a very very convincing arguments as to why I would want anything more than methods (or properties maybe, but they are methods too ;)) in a protocol/interface. I thought the point was that behaviour and implementation ought to be decoupled :).

Whatch Dave's presentation at WWDC on protocol based programming, if he does not convince you, then perhaps nobody will. IMHO the ship of why POP has long sailed... Objc is great, and was extremely innovative 30 years ago. Today there is a team of increadibly talented people bringing a new logic to the table, and if anyone is capable of making it work wonderfully for the next 30 years of apple dev, it is them. Our job is to act like sounding boards for where they want to go, not try to stop them or bring everything back to what they have set out to replace.

If you still have questions about methods in protocols, lookup presentations from brian goetz on the topic, or consider how it is a veeery elegant idiomatic swift solution for the problem of optional protocol methods in objc

One could bends things a bit and say that following a relatively strict TDD approach with genuine unit tests (everything is a mock but the class under test and Apple's own classes perhaps), integration, and automation tests that Objective-C's opt-in strictness (hello -Weverything, lightweight generics, nullability annotations, and various annotations like instancetype, NS_REQUIRES_SUPER, NS_DESIGNATED_INITIALIZER, etc...) is more than a safe enough path that gives you the most freedom and runtime responsibility to be used wisely and productively. Cocoa is a wonderful API to write apps with and yes it is not very Swift like and linked to the Objective-C runtime also in style, but perhaps that says something NCOs about both languages. In Objective-C having message sending/dynamic polymorphism by default never introduced illogical or hard to work through behaviour.

It would be a shame with llvm under the hood to have every single method call resolved against a 13,000 entry hash table today. Ask bertrand serlet what he might have given to have llvm at his disposal when he was working on that part of the runtime.

···

On May 22, 2016, at 8:03 PM, Goffredo Marocchi <panajev@gmail.com> wrote:

On 22 May 2016, at 18:36, L. Mihalkovic via swift-evolution <swift-evolution@swift.org> 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:

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.

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

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.

Lack of elegance? Can you elaborate? But IMHO elegance isn’t what we’re striving for here anyway. The fact that we need to do something at all is already rather inelegant. But it is what it is.

···

On May 22, 2016, at 5:13 PM, L. Mihalkovic <laurent.mihalkovic@gmail.com> wrote:
On May 22, 2016, at 9:53 PM, Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>> wrote:

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

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

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

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

On May 22, 2016, at 1:49 AM, Vladimir.S via swift-evolution <swift-evolution@swift.org <mailto: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 <mailto: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 a method that really ought to be a protocol requirement isn't a
requirement and you don't control the protocol, well you're pretty much out
of luck even today. Any conforming type accessed via the protocol will use
the less efficient extension method and nothing about Brent's proposal
would make that worse or better.

Shadowing of the slow extension method doesn't remove the burden. It may
make calling your fast implementation look nicer, but a less informed user
of your type would unwittingly call the slower implementation if they
access your type via the protocol. You could instead:
* come up with another name for your fast implementation; maybe the
"obvious" name for the method is "frobnicate"--then name your method
"quicklyFrobnicate";
* or, decide you don't want to conform your type to a poorly designed
protocol after all, instead retroactively modeling your type and other
types of interest with a better designed protocol of your making.

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?

I'm not sure I quite understand when this arises. Surely, by construction,
if you wish to retroactively model types, you are willing to design your
protocol around them. What else could it mean to retroactively model
existing types? Can you give a concrete example where during retroactively
modeling you simply have no choice but to name an extension method using a
name that it is shadowed by a conforming type?

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.

You are saying that it would be possible for a protocol extension in one
dependency to conflict with a conforming type in another? This issue can be
avoided if enforcement of non-shadowing by the compiler is such that when
neither conforming type nor protocol extension is under your control
everything continues to work as-is.

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

You don't override protocol requirements, but you do override their default
implementations, whereas you cannot 'override' statically dispatched
extension methods. Within a protocol extension, there are methods that are
overridable by conforming types (i.e. default implementations of protocol
requirements) and methods that are not (i.e. statically dispatched
non-requirements).

···

On Sun, May 22, 2016 at 11:20 PM, Matthew Johnson <matthew@anandabits.com> wrote:

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

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 a method that really ought to be a protocol requirement isn't a requirement and you don't control the protocol, well you're pretty much out of luck even today. Any conforming type accessed via the protocol will use the less efficient extension method and nothing about Brent's proposal would make that worse or better.

Shadowing of the slow extension method doesn't remove the burden. It may make calling your fast implementation look nicer, but a less informed user of your type would unwittingly call the slower implementation if they access your type via the protocol. You could instead:
* come up with another name for your fast implementation; maybe the "obvious" name for the method is "frobnicate"--then name your method "quicklyFrobnicate";
* or, decide you don't want to conform your type to a poorly designed protocol after all, instead retroactively modeling your type and other types of interest with a better designed protocol of your making.

Maybe you want the type to inter operate with code you don't control and in order to do that it must conform to the protocol. And you don't want to obfuscate the interface to the concrete type because the protocol is poorly designed. Conforming to the protocol *is not* the primary reason your type exists - conformance is used only for the purpose of using your type with a specific piece of third party code.

You *could* wrap your type for the purpose of this conformance. This is what a Brent alluded to. But it requires boilerplate and a bunch of conversion operations. This is not just annoying, it could also be complex enough to lead to bugs.

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?

I'm not sure I quite understand when this arises. Surely, by construction, if you wish to retroactively model types, you are willing to design your protocol around them. What else could it mean to retroactively model existing types? Can you give a concrete example where during retroactively modeling you simply have no choice but to name an extension method using a name that it is shadowed by a conforming type?

I'm not saying you have *no choice*. But again, conforming the one problematic type is not the primary purpose for which you are designing the protocol. You know the shadowing method will not present any problems for your design. Why should you be forced to design your protocol around this?

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.

You are saying that it would be possible for a protocol extension in one dependency to conflict with a conforming type in another? This issue can be avoided if enforcement of non-shadowing by the compiler is such that when neither conforming type nor protocol extension is under your control everything continues to work as-is.

So you would still allow the developer to declare the conformance without error? This means that developers still need to understand the shadowing behavior but it is pushed even further into a dark corner of the language with more special case rules that must be learned to understand when you might run into it.

···

Sent from my iPad

On May 22, 2016, at 11:55 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Sun, May 22, 2016 at 11:20 PM, Matthew Johnson <matthew@anandabits.com> wrote:

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

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

You don't override protocol requirements, but you do override their default implementations, whereas you cannot 'override' statically dispatched extension methods. Within a protocol extension, there are methods that are overridable by conforming types (i.e. default implementations of protocol requirements) and methods that are not (i.e. statically dispatched non-requirements).

--
Brent Royal-Gordon
Architechies

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

Sent from my iPad

> 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 a method that really ought to be a protocol requirement isn't a
requirement and you don't control the protocol, well you're pretty much out
of luck even today. Any conforming type accessed via the protocol will use
the less efficient extension method and nothing about Brent's proposal
would make that worse or better.

Shadowing of the slow extension method doesn't remove the burden. It may
make calling your fast implementation look nicer, but a less informed user
of your type would unwittingly call the slower implementation if they
access your type via the protocol. You could instead:
* come up with another name for your fast implementation; maybe the
"obvious" name for the method is "frobnicate"--then name your method
"quicklyFrobnicate";

* or, decide you don't want to conform your type to a poorly designed
protocol after all, instead retroactively modeling your type and other
types of interest with a better designed protocol of your making.

Maybe you want the type to inter operate with code you don't control and
in order to do that it must conform to the protocol. And you don't want to
obfuscate the interface to the concrete type because the protocol is poorly
designed.

I'm not sure this is a reasonable set of demands. I understand a protocol
to be a contract. If you decide to conform to a poorly designed protocol,
you *should* have a poorly designed concrete type. That is the purpose of a
protocol, to provide certain guarantees, be they wise or unwise. To me,
it's a bug rather than a feature to support papering over poor protocol
design when conforming to a protocol, which also forces the poor design to
be exposed only when accessing your type through that protocol. In the end,
you don't have a well-designed type; your type is instead simultaneously
well and poorly designed!

Conforming to the protocol *is not* the primary reason your type exists -
conformance is used only for the purpose of using your type with a specific
piece of third party code.

You *could* wrap your type for the purpose of this conformance. This is
what a Brent alluded to. But it requires boilerplate and a bunch of
conversion operations. This is not just annoying, it could also be complex
enough to lead to bugs.

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?

I'm not sure I quite understand when this arises. Surely, by construction,
if you wish to retroactively model types, you are willing to design your
protocol around them. What else could it mean to retroactively model
existing types? Can you give a concrete example where during retroactively
modeling you simply have no choice but to name an extension method using a
name that it is shadowed by a conforming type?

I'm not saying you have *no choice*. But again, conforming the one
problematic type is not the primary purpose for which you are designing the
protocol. You know the shadowing method will not present any problems for
your design. Why should you be forced to design your protocol around this?

Because, again, a protocol is a contract. If you're retroactively modeling
many well designed types and one poorly designed type, the lowest common
denominator is a poorly designed protocol. That *should* be the result, no?

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.

You are saying that it would be possible for a protocol extension in one
dependency to conflict with a conforming type in another? This issue can be
avoided if enforcement of non-shadowing by the compiler is such that when
neither conforming type nor protocol extension is under your control
everything continues to work as-is.

So you would still allow the developer to declare the conformance without
error? This means that developers still need to understand the shadowing
behavior but it is pushed even further into a dark corner of the language
with more special case rules that must be learned to understand when you
might run into it.

No, I would forbid declaring any such conformance in code the developer
controls. If you control the conforming type and it contains a method name
that clashes with a protocol extension method, you would be forbidden from
declaring conformance without renaming your clashing method. In fact, I'm
beginning to wonder if protocol extension methods on protocols outside the
same module should be internally scoped.

···

On Mon, May 23, 2016 at 6:58 AM, Matthew Johnson <matthew@anandabits.com> wrote:

On May 22, 2016, at 11:55 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Sun, May 22, 2016 at 11:20 PM, Matthew Johnson <matthew@anandabits.com> > wrote:

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

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

You don't override protocol requirements, but you do override their
default implementations, whereas you cannot 'override' statically
dispatched extension methods. Within a protocol extension, there are
methods that are overridable by conforming types (i.e. default
implementations of protocol requirements) and methods that are not (i.e.
statically dispatched non-requirements).

--
Brent Royal-Gordon
Architechies

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

Sent from my iPad

> 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 a method that really ought to be a protocol requirement isn't a requirement and you don't control the protocol, well you're pretty much out of luck even today. Any conforming type accessed via the protocol will use the less efficient extension method and nothing about Brent's proposal would make that worse or better.

Shadowing of the slow extension method doesn't remove the burden. It may make calling your fast implementation look nicer, but a less informed user of your type would unwittingly call the slower implementation if they access your type via the protocol. You could instead:
* come up with another name for your fast implementation; maybe the "obvious" name for the method is "frobnicate"--then name your method "quicklyFrobnicate";
* or, decide you don't want to conform your type to a poorly designed protocol after all, instead retroactively modeling your type and other types of interest with a better designed protocol of your making.

Maybe you want the type to inter operate with code you don't control and in order to do that it must conform to the protocol. And you don't want to obfuscate the interface to the concrete type because the protocol is poorly designed.

I'm not sure this is a reasonable set of demands. I understand a protocol to be a contract. If you decide to conform to a poorly designed protocol, you *should* have a poorly designed concrete type.

This is crazy. If a protocol happens to place a method in an extension rather than making it a default implementation of a requirement and I want to conform to it does not mean my concrete type should have be poorly designed. Sure, when the 3rd party type uses my concrete type via the interface of its protocol it will not receive the benefits of a higher performance implementation. But maybe interoperability is more important than the highest performance possible.

That is the purpose of a protocol, to provide certain guarantees, be they wise or unwise. To me, it's a bug rather than a feature to support papering over poor protocol design when conforming to a protocol, which also forces the poor design to be exposed only when accessing your type through that protocol. In the end, you don't have a well-designed type; your type is instead simultaneously well and poorly designed!

When it comes to interoperating with code you do not control you get what you get. You have to work with it one way or another. What I am saying is that the language should not artificially limit our options here.

Conforming to the protocol *is not* the primary reason your type exists - conformance is used only for the purpose of using your type with a specific piece of third party code.

You *could* wrap your type for the purpose of this conformance. This is what a Brent alluded to. But it requires boilerplate and a bunch of conversion operations. This is not just annoying, it could also be complex enough to lead to bugs.

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?

I'm not sure I quite understand when this arises. Surely, by construction, if you wish to retroactively model types, you are willing to design your protocol around them. What else could it mean to retroactively model existing types? Can you give a concrete example where during retroactively modeling you simply have no choice but to name an extension method using a name that it is shadowed by a conforming type?

I'm not saying you have *no choice*. But again, conforming the one problematic type is not the primary purpose for which you are designing the protocol. You know the shadowing method will not present any problems for your design. Why should you be forced to design your protocol around this?

Because, again, a protocol is a contract. If you're retroactively modeling many well designed types and one poorly designed type, the lowest common denominator is a poorly designed protocol. That *should* be the result, no?

It all depends on the types and the protocol. Protocols often represent a small subset of the total interface and functionality of a type.

The fact that the type may happen to use a name for a method that matches a name you would like to use in an extension to your protocol does not necessarily mean either is poorly designed.

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.

You are saying that it would be possible for a protocol extension in one dependency to conflict with a conforming type in another? This issue can be avoided if enforcement of non-shadowing by the compiler is such that when neither conforming type nor protocol extension is under your control everything continues to work as-is.

So you would still allow the developer to declare the conformance without error? This means that developers still need to understand the shadowing behavior but it is pushed even further into a dark corner of the language with more special case rules that must be learned to understand when you might run into it.

No, I would forbid declaring any such conformance in code the developer controls. If you control the conforming type and it contains a method name that clashes with a protocol extension method, you would be forbidden from declaring conformance without renaming your clashing method. In fact, I'm beginning to wonder if protocol extension methods on protocols outside the same module should be internally scoped.

In this case you *don’t* control the conforming type. You want to declare conformance for a type that is in a 3rd party library. You said when neither is under your control you would leave things as-is. Today you can declare conformance without error. Would you still allow that?

···

On May 23, 2016, at 10:50 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Mon, May 23, 2016 at 6:58 AM, Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>> wrote:
On May 22, 2016, at 11:55 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:

On Sun, May 22, 2016 at 11:20 PM, Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>> wrote:

On May 22, 2016, at 4:22 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto: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:

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

You don't override protocol requirements, but you do override their default implementations, whereas you cannot 'override' statically dispatched extension methods. Within a protocol extension, there are methods that are overridable by conforming types (i.e. default implementations of protocol requirements) and methods that are not (i.e. statically dispatched non-requirements).

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

> 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 a method that really ought to be a protocol requirement isn't a
requirement and you don't control the protocol, well you're pretty much out
of luck even today. Any conforming type accessed via the protocol will use
the less efficient extension method and nothing about Brent's proposal
would make that worse or better.

Shadowing of the slow extension method doesn't remove the burden. It may
make calling your fast implementation look nicer, but a less informed user
of your type would unwittingly call the slower implementation if they
access your type via the protocol. You could instead:
* come up with another name for your fast implementation; maybe the
"obvious" name for the method is "frobnicate"--then name your method
"quicklyFrobnicate";

* or, decide you don't want to conform your type to a poorly designed
protocol after all, instead retroactively modeling your type and other
types of interest with a better designed protocol of your making.

Maybe you want the type to inter operate with code you don't control and
in order to do that it must conform to the protocol. And you don't want to
obfuscate the interface to the concrete type because the protocol is poorly
designed.

I'm not sure this is a reasonable set of demands. I understand a protocol
to be a contract. If you decide to conform to a poorly designed protocol,
you *should* have a poorly designed concrete type.

This is crazy. If a protocol happens to place a method in an extension
rather than making it a default implementation of a requirement and I want
to conform to it does not mean my concrete type should have be poorly
designed.

Quite simply, I think it should.

Sure, when the 3rd party type uses my concrete type via the interface of
its protocol it will not receive the benefits of a higher performance
implementation. But maybe interoperability is more important than the
highest performance possible.

As you have said, the workaround is to provide two classes, one wrapping
the other. One provides interoperability, the other high performance.

That is the purpose of a protocol, to provide certain guarantees, be they
wise or unwise. To me, it's a bug rather than a feature to support papering
over poor protocol design when conforming to a protocol, which also forces
the poor design to be exposed only when accessing your type through that
protocol. In the end, you don't have a well-designed type; your type is
instead simultaneously well and poorly designed!

When it comes to interoperating with code you do not control you get what
you get. You have to work with it one way or another. What I am saying is
that the language should not artificially limit our options here.

I wouldn't call it an 'artificial limit,' at least any more than any other
aspect of language design. The language must determine what it means to
conform a type to a protocol. One option, which I think is entirely valid,
is to decide that extensions on a protocol are not to be shadowed by a
conforming type.

Conforming to the protocol *is not* the primary reason your type exists
- conformance is used only for the purpose of using your type with a
specific piece of third party code.

You *could* wrap your type for the purpose of this conformance. This is
what a Brent alluded to. But it requires boilerplate and a bunch of
conversion operations. This is not just annoying, it could also be complex
enough to lead to bugs.

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?

I'm not sure I quite understand when this arises. Surely, by
construction, if you wish to retroactively model types, you are willing to
design your protocol around them. What else could it mean to retroactively
model existing types? Can you give a concrete example where during
retroactively modeling you simply have no choice but to name an extension
method using a name that it is shadowed by a conforming type?

I'm not saying you have *no choice*. But again, conforming the one
problematic type is not the primary purpose for which you are designing the
protocol. You know the shadowing method will not present any problems for
your design. Why should you be forced to design your protocol around this?

Because, again, a protocol is a contract. If you're retroactively modeling
many well designed types and one poorly designed type, the lowest common
denominator is a poorly designed protocol. That *should* be the result, no?

It all depends on the types and the protocol. Protocols often represent a
small subset of the total interface and functionality of a type.

The fact that the type may happen to use a name for a method that matches
a name you would like to use in an extension to your protocol does not
necessarily mean either is poorly designed.

I agree. Both the type and the protocol might be beautifully designed. But
I would argue that conforming the one to the other might be a poor design.

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.

You are saying that it would be possible for a protocol extension in one
dependency to conflict with a conforming type in another? This issue can be
avoided if enforcement of non-shadowing by the compiler is such that when
neither conforming type nor protocol extension is under your control
everything continues to work as-is.

So you would still allow the developer to declare the conformance without
error? This means that developers still need to understand the shadowing
behavior but it is pushed even further into a dark corner of the language
with more special case rules that must be learned to understand when you
might run into it.

No, I would forbid declaring any such conformance in code the developer
controls. If you control the conforming type and it contains a method name
that clashes with a protocol extension method, you would be forbidden from
declaring conformance without renaming your clashing method. In fact, I'm
beginning to wonder if protocol extension methods on protocols outside the
same module should be internally scoped.

In this case you *don’t* control the conforming type. You want to declare
conformance for a type that is in a 3rd party library. You said when
neither is under your control you would leave things as-is. Today you can
declare conformance without error. Would you still allow that?

In that scenario, I would not. The type you don't control simply cannot
conform to the protocol you don't control. Again, there would be
workarounds.

···

On Mon, May 23, 2016 at 12:13 PM, Matthew Johnson <matthew@anandabits.com> wrote:

On May 23, 2016, at 10:50 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Mon, May 23, 2016 at 6:58 AM, Matthew Johnson <matthew@anandabits.com> > wrote:

On May 22, 2016, at 11:55 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Sun, May 22, 2016 at 11:20 PM, Matthew Johnson <matthew@anandabits.com >> > wrote:

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

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

You don't override protocol requirements, but you do override their
default implementations, whereas you cannot 'override' statically
dispatched extension methods. Within a protocol extension, there are
methods that are overridable by conforming types (i.e. default
implementations of protocol requirements) and methods that are not (i.e.
statically dispatched non-requirements).

--
Brent Royal-Gordon
Architechies

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

Just to have the whole picture for those, who is reading your discussion with Matthew : yes, extensions(that were not declared in protocol itself) are not shadowed by a conforming type(if called by reference which type is protocol, i.e let p = MyProtocol = MyType()). And I agree, that when you know this - this is a simple rule.

But, you have to remember another rules:
* default implementation of protocol requirement (in protocol extension) - *will* be shadowed by type's method *if* implemented *exactly* in the same type that conformed to protocol
* default implementation of protocol requirement (in protocol extension) - will *not* be shadowed *if* implemented in derived type which base type conformed to protocol (i.e. type has implemented the method, type is conformed to protocol, but protocol's default implementation will be called)

For me (now), it seems like these rules has strong reasons and we will not change them. OK. But IMO we definitely needs some help from Swift to avoid possible *not easy to fix* errors related to these rules.

···

On 23.05.2016 23:09, Xiaodi Wu via swift-evolution wrote:

One option, which I think is entirely valid, is to decide that extensions
on a protocol are not to be shadowed by a conforming type.