[swift-evolution] Overriding protocol default implementation

Rod Brown rodney.brown6 at icloud.com
Fri Feb 10 23:32:43 CST 2017


I see your point. I think the core issue here is customizability vs safety.

Allowing users to perform actions in these functions that change the behaviour of a protocol is potentially dangerous, and therefore Swift as a safe language disallows it.

This comes at the cost of being able to override the function dynamically and provide a different implementation that is perhaps more appropriate. This limits Swift flexibility.

Depending on where you come down on this, it may be "too conservative". Things can only be safe to a certain level and if we keep optimising for safety over functionality, perhaps we'll restrict Swift too much for it to be a useful language. Or perhaps it's a good trade. The decision regarding "closed by default" also had considerations like retrospectively "finalising" or "opening" APIs that made those decisions a little easier.

> On 11 Feb 2017, at 3:32 pm, Xiaodi Wu <xiaodi.wu at gmail.com> wrote:
> 
>> On Fri, Feb 10, 2017 at 9:54 PM, Rod Brown <rodney.brown6 at icloud.com> wrote:
>> 
>> 
>> Sent from my iPhone
>> 
>>> On 11 Feb 2017, at 2:20 pm, Xiaodi Wu <xiaodi.wu at gmail.com> wrote:
>>> 
>>>> On Fri, Feb 10, 2017 at 6:59 PM, Rod Brown <rodney.brown6 at icloud.com> wrote:
>>>> I don't believe these two worlds are in conflict at all.
>>>> 
>>>> We currently have the default only state (without overrides), plus a conflict. We would simply be adding "override" functionality in part to clear the conflict.
>>>> 
>>>> With POP the idea is that the protocol, in knowing about how it is constituted, has a clear pattern for how it implements its behaviours. Therefore you can leverage the protocol to bear the brunt of the work, just as we do now.
>>>> 
>>>> The additional tweak to the design of protocols is to allow a user to define their own implementation where the default is not appropriate, or is incomplete for the use case.
>>> 
>>> I think I've written a poor explanation of my point. In Swift, it is the library _author_, not the _user_, who gets the final say as to the upper limits on what users can do with the author's types, by using modifiers such as `final` and `open` (or the lack thereof). This has been the subject of vehement opposition but, nonetheless, it is a clear and opinionated decision on the part of the language. What you are critiquing as a bug is regarded as a feature. That is to say, it is a way for the author of a protocol extension method to deny to the user a customization point (in other words, to disallow the outright overriding of the "default" behavior).
>>>  
>>>> This doesn't work against POP - it simply observes that implementations at times may need to be customised to the use case, and allows that, as an optional override.
>>> 
>>> Again, the status quo in Swift is that it is up to the protocol's author to determine which methods are overridable by the user and which are not. The idea is that protocol extension methods that are not protocol requirements are the intended way for disallowing such overriding.
>>> 
>>> As to its practical use: this guarantees that if protocol `P` has a method `foo()`, it is possible to invoke `foo()` on an instance of existential type `P` knowing that you will invoke the intended method. A concrete type `T` that conforms to `P` may have its own `foo()` with totally different semantics. After all, if `foo()` is not a protocol requirement, then conforming types can have their own `foo()` do anything at all, with the collision in name being mere coincidence and no guarantee of similar semantics. And, with extensions, some third party can implement such a `foo()` on `T` that the library author has no way of reasoning about. By having a "shadowing" feature for `foo()`, I can know that no matter how anyone in the future extends type `T`, I will invoke the intended `foo()` with the right semantics on an instance of existential type `P`.
>>>  
>>>> It's clear people are trying to do this already because they already have overrides that are causing this conflict, and thus we are having the discussion.
>>> 
>>> It sounds like what you are saying is that users of libraries are trying to "override" protocol extension methods that authors of libraries have designed not to be overridden. That this causes problems is, afaict, the intended consequence of this feature and not an overlooked bug. For maximum flexibility, however, Swift allows you to "shadow" the non-overridable method with an identically named method of your own design that can have different semantics.
>> 
>> If the intent is to restrict users and to provide the power to the author of the protocol, it appears not just opinionated, but arrogant.
>> 
>> As software developers, we need to work together to provide solutions to use cases. This includes not understanding what some use case may be. But instead, the consistent argument here has been that software should be as it is designed, to hell with the users of the frameworks who may have an unknown use case.
>> 
>> This therefore has created the shadow problem, where people implementing a protocol end up colliding with the same name, and must make casts back.
>> 
>> Why can't we make an adjustment and allow protocol implementers to define the cases where the protocol action is inappropriate.
>> 
>> I believe this argument stems back to the closed-by-default argument on classes with "public" and shows a concerning trend in how Swift is being developed. We cannot look at Swift purely from the perspective of the Standard Library, or one framework, or one implementer. It is how we coordinate between framework authors that we end up creating a unified system.
>> 
>> It should never be a feature to make users of frameworks fight with writers of frameworks. That's just absurd.
> 
> That's an unfair characterization. Certainly, `public` vs `open` was hotly debated, but the Swift core team came down on one side of that debate after weighing all the arguments. As you say, this functionality of protocol extension members is of a kind to that decision, and in fact it pre-dates that decision.
> 
> Let's be clear that it's not about making users of libraries "fight" with authors of libraries. In a language where a user can retroactively extend a type, these facilities can be critical for allowing library authors to reason about their own code. These guarantees aren't only for the original author, either. See the currently ongoing thread about introducing a `closed` keyword and how knowledge about restrictions on how a class, enum, struct, or protocol can be changed by another party can help any user reason about their own code. As you can see above, I offered an example where even an end user can rely on `P.foo()` to do the same intended thing (where `P` is a protocol) no matter how anyone else extends a conforming type `T`. This is not at all absurd but a practical boon.
> 
> Can it be inconvenient if you happen to be the user of a type `P` and want a customization point where the author has not designed one? Sure. But no more so than if you happen to be the user of a type `P` and want to access a private member where the author has decided not to make it public. If we deem this all absurd we'd not have access modifiers either, and we'd abandon all the work on the roadmap about library resilience, which can also constrain library authors to allow more freedom for library users.
>  
>>>> In this case, I don't see overriding the protocol "default" as working against this world - I think it clarifies it.
>>>> 
>>>> Rod
>>> 
> 
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170211/dd5ee254/attachment.html>


More information about the swift-evolution mailing list