[swift-evolution] [Idea] How to eliminate 'optional' protocol requirements

Douglas Gregor dgregor at apple.com
Wed Apr 13 12:24:23 CDT 2016


> On Apr 12, 2016, at 2:32 PM, Dietmar Planitzer via swift-evolution <swift-evolution at swift.org> wrote:
> 
> Inline.
> 
>> On Apr 11, 2016, at 10:03, Dave Abrahams <dabrahams at apple.com> wrote:
>> 
>> 
>> on Sun Apr 10 2016, Dietmar Planitzer <dplanitzer-AT-q.com> wrote:
>> 
>>>> On Apr 10, 2016, at 11:46, Dave Abrahams via swift-evolution
>>> <swift-evolution at swift.org> wrote:
>>>> 
>>>> 
>>>> on Sun Apr 10 2016, Dietmar Planitzer <swift-evolution at swift.org>
>>> wrote:
>>>> 
>>>>> I’m not sure whether you’ve read the conclusion of my mail since
>>>>> you’ve only commented on the introductory part. In the conclusion I
>>>>> wrote that a possible approach for the replacement of ObjC-style
>>>>> optional protocol methods would be:
>>>>> 
>>>>> 1) the default implementation of a protocol method must be defined
>>> in
>>>>> the protocol (so just like in native Swift protocols today).
>>>> 
>>>> ? They can and must be defined in protocol extensions today.
>>> 
>>> I know.
>>> 
>>>>> 
>>>>> 2) we add a way for a protocol provider to check whether the
>>> protocol
>>>>> adopter has provided an “override” of the default method.
>>>> 
>>>> I object to this part.
>>> 
>>> You object why? I do understand why you object to the ObjC model since
>>> there is not necessarily an implementation of the protocol method and
>>> thus the protocol provider has to guard every call with an existence
>>> check. But in this model here we would be guaranteed that there would
>>> be an implementation of the protocol method and thus guarding the call
>>> wouldn’t be necessary.
>> 
>> Because it's a needless complication that will encourage protocol and
>> algorithm designers to create inefficient programs because they know the
>> user can fall back on this hack.
> 
> It’s not clear why you think that the ability to check whether a protocol adopter implements a method or doesn’t would make programs inefficient. The fact that the protocol provider can check whether an adopter actually wants a feature or not (optional method is implemented -> adopter wants the feature; otherwise he clearly doesn’t) is what enables us to create more efficient implementations.

Allowing a user of a protocol to dynamically check whether the type conforming to the protocol requests some bit of custom functionality allows fast paths for non-customized behavior. The primary mechanism for that check in Objective-C is -respondsToSelector:, but that does not imply that it is the right mechanism for Swift.

>>> 
>>> Note that has_override() is just a placeholder syntax because I’ve not
>>> had a good idea yet of how to express this in a Swiftier way.
>> 
>>  if delegate.hasVariableSizedRows { … }
> 
> This means that we not only increase the surface of the API, it also means that we make it now possible for the protocol adopter to provide conflicting information to the caller of the protocol methods. At the same time, the caller of the protocol methods still needs to guard every call of tableView(heightForRow:) with a call to hasVariableSizedRows(). So by the end of the day, this solution doesn’t improve anything. It just adds new complexity and sources of bugs.

This particular API has been discussed numerous times. For example, here’s a potentially-more-Swifty approach to it:

	http://thread.gmane.org/gmane.comp.lang.swift.evolution/13347/focus=13608

The approach eliminates the coupling between, e.g., hasVariableSizedRows and heightForRow and makes it far more clear (IMO) that you’re choosing among several behaviors. 

> in ObjC is the idea that the NSTableView is a component which offers a set of features:
> 
> - fixed row heights
> - variable row heights
> - drag & drop
> - old style pasteboard support
> - new style pasteboard support
> - old style table cells
> - new style table cell views
> - etc, pp

That’s a lot of disparate functionality in one very large protocol. Again, referring to the other thread, Brent started dividing this delegate into a number of related protocols:

	http://thread.gmane.org/gmane.comp.lang.swift.evolution/13347/focus=13601



> and then the delegate simply picks the features that it wants to use. It simply does this by implementing the corresponding methods and by NOT implementing the methods of a feature it doesn’t care about:
> 
> - delegate wants feature X -> implement the method for feature X
> 
> - delegate does not want feature X -> nothing to do
> 
> and this is why the concept of optional protocol methods captures this idea precisely: I don’t need to write code for feature X if I don’t want it. Why should I write code for something I don’t want? Wouldn’t make sense.

Everything you say in the above snippet applies equally to default implementations of protocol members.

>>  if !(delegate is NSUniformTableViewDelegate) { … }
> 
> I don’t think that replacing 1 protocol with a dozen or more protocols is a good idea or a step forward. Beside that we would actually have to introduce hundreds of new protocols since the optional protocol method feature is used all over the place in public Mac OS X frameworks and also in private ones.

I might agree with that statement, but the same complexity is there whether you split a large protocol into several related protocols or not: having multiple protocols merely acknowledges and groups that functionality in code rather than in documentation. Note how the documentation for NSTableViewDelegate has to be split into numerous groups of methods because it’s covering a number of different aspects of table views:

	https://developer.apple.com/library/mac/documentation/Cocoa/Reference/NSTableViewDelegate_Protocol/


>> etc.
>>> 
>>> In this example the table view is able to check whether the protocol
>>> adopter has actually “overriden” the default implementation of
>>> tableView(_:, heightForRow:). 
>> 
>> Which, IMO, is a terrible way to indicate that a view has variable row
>> heights.  It's indirect and maybe even inaccurate (I can imagine a table
>> view that is uniform and has its height set up once at construction
>> time, therefore it needs to override heightForRow).
> 
> I don’t see how it is indirect. I want the feature -> I implement the corresponding method; I don’t want the feature -> I don’t write code for it.

“I want the feature -> I implement the corresponding protocol” is nearly identical, but ties the action specifically to a named entity in the language. It’s also how one *always* deals with protocols.

> This captures exactly the nature of the problem and it really can’t get any simpler than that for the protocol user. 

From the perspective of the person making their type conform to a protocol (what you’re calling a protocol “user”), there is absolutely no difference between “has a default implementation” and “is an optional requirement”.

	- Doug

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160413/71af5b7c/attachment.html>


More information about the swift-evolution mailing list