[Proposal] Make optional protocol methods first class citizens


(Yuval Tal) #1

Hi,

I find that optional protocol methods to be very useful. However, there is
a caveat -- it needs to be mapped to @objc. This puts a set of limitations,
such as: structures cannot be used as parameters as it does not map to
objective-c. What do you think about removing the requirement of using
@objc and allow to create optional methods without these limitations?

Thank you,

-Yuval


(Haravikk) #2

I’m not sure, why not just define an additional protocol with the optional method(s) you’d like to add? The whole point of protocols is to guarantee access to some baseline capability, so I’m not sure that optional capabilities are well covered except by adding new protocols that extend each other.

Objective-C conversion has the ability because Objective-C is unusual by comparison thanks to its ability to have “method calls” (messages) with no actual destination. I think it’s good for Swift to be more structured and strict than that.

···

On 30 Mar 2016, at 15:08, Yuval Tal via swift-evolution <swift-evolution@swift.org> wrote:

Hi,

I find that optional protocol methods to be very useful. However, there is a caveat -- it needs to be mapped to @objc. This puts a set of limitations, such as: structures cannot be used as parameters as it does not map to objective-c. What do you think about removing the requirement of using @objc and allow to create optional methods without these limitations?

Thank you,

-Yuval


(Andrey Tarantsov) #3

I'm missing those optional methods too, but protocol extensions sound like a better solution for this.

(For those suggesting a separate protocol, consider UITableView. How many protocols would it take to model all the optional delegate methods as separate protocols? Certainly more than 10, perhaps a few dozen.)

I would welcome a standardized way to document the methods as optional-to-implement, though, beyond just requiring a protocol extension. My ideal option would be to allow the optional keyword and change it to simply require a default implementation in a protocol extension.

If we don't want a language change, then perhaps a conventional doc tag?

A.

···

On Mar 30, 2016, at 8:08 PM, Yuval Tal via swift-evolution <swift-evolution@swift.org> wrote:

Hi,

I find that optional protocol methods to be very useful. However, there is a caveat -- it needs to be mapped to @objc. This puts a set of limitations, such as: structures cannot be used as parameters as it does not map to objective-c. What do you think about removing the requirement of using @objc and allow to create optional methods without these limitations?

Thank you,

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


(Dave Abrahams) #4

Caveat: this is going to be strongly-worded; sorry in advance. I think
(no offense intended) it's a terrible idea. The whole notion of an
“optional requirement” is nonsensical to begin with, and the use of
optional protocol requirements encourages a style of programming that
lifts the responsibility of the protocol designer for careful design at
the expense of clients of the protocol. There are better ways to do
things; let's not propagate this anti-pattern any further than it's
already gone.

···

on Wed Mar 30 2016, Yuval Tal <swift-evolution@swift.org> wrote:

Hi,

I find that optional protocol methods to be very useful. However,
there is a caveat -- it needs to be mapped to @objc. This puts a set
of limitations, such as: structures cannot be used as parameters as it
does not map to objective-c. What do you think about removing the
requirement of using @objc and allow to create optional methods
without these limitations?

--
Dave


(Adrian Kashivskyy) #5

I think it's a bad idea because current model (with no optional methods) encourages decomposing protocols into smaller ones, thus discouraging partial functions and making our code safer and more predictable.

In my opinion, right now, if you really need an optional protocol method, consider adding a default implementation for it. You'd be surprised how often it's enough to satisfy the need for an optional method.

Regards,
Adrian Kashivskyy

···

Wiadomość napisana przez Yuval Tal via swift-evolution <swift-evolution@swift.org> w dniu 30.03.2016, o godz. 16:08:

Hi,

I find that optional protocol methods to be very useful. However, there is a caveat -- it needs to be mapped to @objc. This puts a set of limitations, such as: structures cannot be used as parameters as it does not map to objective-c. What do you think about removing the requirement of using @objc and allow to create optional methods without these limitations?

Thank you,

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


(Kurt Werle) #6

Another alternative is to add a protocol extension with
blank/harmless/default methods for things that are optional and always call
'em.

···

On Wed, Mar 30, 2016 at 7:18 AM, Haravikk via swift-evolution < swift-evolution@swift.org> wrote:

I’m not sure, why not just define an additional protocol with the optional
method(s) you’d like to add? The whole point of protocols is to guarantee
access to some baseline capability, so I’m not sure that optional
capabilities are well covered except by adding new protocols that extend
each other.

Objective-C conversion has the ability because Objective-C is unusual by
comparison thanks to its ability to have “method calls” (messages) with no
actual destination. I think it’s good for Swift to be more structured and
strict than that.

> On 30 Mar 2016, at 15:08, Yuval Tal via swift-evolution < > swift-evolution@swift.org> wrote:
>
> Hi,
>
> I find that optional protocol methods to be very useful. However, there
is a caveat -- it needs to be mapped to @objc. This puts a set of
limitations, such as: structures cannot be used as parameters as it does
not map to objective-c. What do you think about removing the requirement of
using @objc and allow to create optional methods without these limitations?
>
> Thank you,
>
> -Yuval
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

--
kurt@CircleW.org
http://www.CircleW.org/kurt/


(Yuval Tal) #7

Hello,

I'm aware of all the protocol/extension or multiple protocol options. I
think that for the sake of code readability and the ability to easily
create components, it is extremely useful. For example, if I want to create
a class that has a delegate, it is extremely useful for the caller to
easily know what is required to be implemented and what is optional. Having
multiple protocols is probably not a great idea, and using the protocol
extension method makes it non-intuitive for the caller to clearly
understand what is optional. Note that I'm not suggesting a new language
constructs, just to ease the requirement on existing one, that honestly, I
found extremely clear and making interfaces much more obvious.

-Yuval

···

On Wed, Mar 30, 2016 at 10:18 AM, Haravikk <swift-evolution@haravikk.me> wrote:

I’m not sure, why not just define an additional protocol with the optional
method(s) you’d like to add? The whole point of protocols is to guarantee
access to some baseline capability, so I’m not sure that optional
capabilities are well covered except by adding new protocols that extend
each other.

Objective-C conversion has the ability because Objective-C is unusual by
comparison thanks to its ability to have “method calls” (messages) with no
actual destination. I think it’s good for Swift to be more structured and
strict than that.

> On 30 Mar 2016, at 15:08, Yuval Tal via swift-evolution < > swift-evolution@swift.org> wrote:
>
> Hi,
>
> I find that optional protocol methods to be very useful. However, there
is a caveat -- it needs to be mapped to @objc. This puts a set of
limitations, such as: structures cannot be used as parameters as it does
not map to objective-c. What do you think about removing the requirement of
using @objc and allow to create optional methods without these limitations?
>
> Thank you,
>
> -Yuval


(Sean Heber) #8

Caveat: this is going to be strongly-worded; sorry in advance. I think
(no offense intended) it's a terrible idea. The whole notion of an
“optional requirement” is nonsensical to begin with, and the use of
optional protocol requirements encourages a style of programming that
lifts the responsibility of the protocol designer for careful design at
the expense of clients of the protocol. There are better ways to do
things; let's not propagate this anti-pattern any further than it's
already gone.

This is why my suggestion to solve this required the protocol to include default values for any optional function with a return value - I don’t think that a function should be optional as in “no presence” or even in a way that the caller can identify if the function is or isn’t defined, but just optional as in “it has a clear documented default.” I don’t agree that the caller of an optional protocol function should be able to tell if the function is present or not - the protocol itself should be defined as if all functions are there, but the “optional-ness” is then no worse than default values on function parameters - but that might just be me.

Perhaps a better term for my way of thinking about it would be “default” rather than optional:

protocol UIGestureRecognizerDelegate {
default func gestureRecognizerShouldBegin(gestureRecognizer: UIGestureRecognizer) -> Bool = true
// etc
}

l8r
Sean


(Yuval Tal) #9

None taken. However, most of the delegate concept of UIKit relies heavily
on this "nonsensical" requirement. It is impossible for someone to
implement a control in swift which is "in the spirit" of UIKit, meaning the
control has a delegate, with several methods that share the same name with
different parameters, some are required and some are optional. I think it
is not fair to tell users that they cannot implement something that is such
a common and repeating pattern in the core.

···

On Thu, Mar 31, 2016 at 2:23 PM, Dave Abrahams via swift-evolution < swift-evolution@swift.org> wrote:

on Wed Mar 30 2016, Yuval Tal <swift-evolution@swift.org> wrote:

> Hi,
>
> I find that optional protocol methods to be very useful. However,
> there is a caveat -- it needs to be mapped to @objc. This puts a set
> of limitations, such as: structures cannot be used as parameters as it
> does not map to objective-c. What do you think about removing the
> requirement of using @objc and allow to create optional methods
> without these limitations?

Caveat: this is going to be strongly-worded; sorry in advance. I think
(no offense intended) it's a terrible idea. The whole notion of an
“optional requirement” is nonsensical to begin with, and the use of
optional protocol requirements encourages a style of programming that
lifts the responsibility of the protocol designer for careful design at
the expense of clients of the protocol. There are better ways to do
things; let's not propagate this anti-pattern any further than it's
already gone.

--
Dave

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


(David Waite) #10

+1

We need to support optional methods in @objc protocols because it is required for interoperability. However, there are in my experience only reasons for an optional protocol method:

- There is a very common default behavior for a delegate (such as return immediately with a value of 0/nil/false) which would be a burden to require everyone to implement. We can instead accomplish this with protocol extensions

- The absence of a method in a delegate indicates a behavior that is otherwise impossible for the delegate to implement. The easiest example of this would be tableView:heightForRowAtIndexPath:, where the absence triggers an optimization where UITableView.rowHeight is used for all math, and its presence means that performance will be degraded, and the delegate will be called repeatedly for every single index path in order to figure out the table positioning/scroll height. I believe this has been shown to be much better solved via an optional additional protocol conformance to indicate that you are supporting a different ‘class’ of behavior, in this case that might be something like UITableViewVaribleHeightDelegate.

I’d hate for there to be a push for optional protocol methods just because it can be confusing which methods are required in swift protocol implementations (vs defaulted by a protocol extension).

-DW

···

On Mar 31, 2016, at 12:23 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:

on Wed Mar 30 2016, Yuval Tal <swift-evolution@swift.org> wrote:

Hi,

I find that optional protocol methods to be very useful. However,
there is a caveat -- it needs to be mapped to @objc. This puts a set
of limitations, such as: structures cannot be used as parameters as it
does not map to objective-c. What do you think about removing the
requirement of using @objc and allow to create optional methods
without these limitations?

Caveat: this is going to be strongly-worded; sorry in advance. I think
(no offense intended) it's a terrible idea. The whole notion of an
“optional requirement” is nonsensical to begin with, and the use of
optional protocol requirements encourages a style of programming that
lifts the responsibility of the protocol designer for careful design at
the expense of clients of the protocol. There are better ways to do
things; let's not propagate this anti-pattern any further than it's
already gone.

--
Dave

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


(Austin Zheng) #11

This shouldn't be an issue in practice, though, since any UIKit control subclass has to inherit from NSObject anyways, which means they can always conform to any @objc protocol.

The main utility of removing the @objc requirement for optional protocol requirements would to allow their use in Swift code oriented away from Cocoa/Foundation, which is part of what Dave is objecting to.

Austin

···

On Mar 31, 2016, at 11:49 AM, Yuval Tal via swift-evolution <swift-evolution@swift.org> wrote:

None taken. However, most of the delegate concept of UIKit relies heavily on this "nonsensical" requirement. It is impossible for someone to implement a control in swift which is "in the spirit" of UIKit, meaning the control has a delegate, with several methods that share the same name with different parameters, some are required and some are optional. I think it is not fair to tell users that they cannot implement something that is such a common and repeating pattern in the core.

On Thu, Mar 31, 2016 at 2:23 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

on Wed Mar 30 2016, Yuval Tal <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

> Hi,
>
> I find that optional protocol methods to be very useful. However,
> there is a caveat -- it needs to be mapped to @objc. This puts a set
> of limitations, such as: structures cannot be used as parameters as it
> does not map to objective-c. What do you think about removing the
> requirement of using @objc and allow to create optional methods
> without these limitations?

Caveat: this is going to be strongly-worded; sorry in advance. I think
(no offense intended) it's a terrible idea. The whole notion of an
“optional requirement” is nonsensical to begin with, and the use of
optional protocol requirements encourages a style of programming that
lifts the responsibility of the protocol designer for careful design at
the expense of clients of the protocol. There are better ways to do
things; let's not propagate this anti-pattern any further than it's
already gone.

--
Dave

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto: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


(Robert Widmann) #12

To be fair, the "spirit of UIKit" that you mention comes from a time and language that encouraged OO and OO alone. Optional methods are, more often than not, backed by gigantic bit fields that keep track of whether or not the delegate actually conforms to the entirety of a protocol, which opens up a huge vector for bugs and complicates the internal logic of the control. I think Swift gives us a better opportunity to rethink the old approach not encourage it, don't you think?

For example, using a method returning an optional (or even better, a proper value) means you can give a proper semantics for what it means to not implement a particular method rather than "no selector here".

~Robert Widmann

2016/03/31 14:49、Yuval Tal via swift-evolution <swift-evolution@swift.org> のメッセージ:

···

None taken. However, most of the delegate concept of UIKit relies heavily on this "nonsensical" requirement. It is impossible for someone to implement a control in swift which is "in the spirit" of UIKit, meaning the control has a delegate, with several methods that share the same name with different parameters, some are required and some are optional. I think it is not fair to tell users that they cannot implement something that is such a common and repeating pattern in the core.

On Thu, Mar 31, 2016 at 2:23 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:

on Wed Mar 30 2016, Yuval Tal <swift-evolution@swift.org> wrote:

> Hi,
>
> I find that optional protocol methods to be very useful. However,
> there is a caveat -- it needs to be mapped to @objc. This puts a set
> of limitations, such as: structures cannot be used as parameters as it
> does not map to objective-c. What do you think about removing the
> requirement of using @objc and allow to create optional methods
> without these limitations?

Caveat: this is going to be strongly-worded; sorry in advance. I think
(no offense intended) it's a terrible idea. The whole notion of an
“optional requirement” is nonsensical to begin with, and the use of
optional protocol requirements encourages a style of programming that
lifts the responsibility of the protocol designer for careful design at
the expense of clients of the protocol. There are better ways to do
things; let's not propagate this anti-pattern any further than it's
already gone.

--
Dave

_______________________________________________
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


(Chris Lattner) #13

None taken. However, most of the delegate concept of UIKit relies heavily on this "nonsensical" requirement. It is impossible for someone to implement a control in swift which is "in the spirit" of UIKit, meaning the control has a delegate, with several methods that share the same name with different parameters, some are required and some are optional. I think it is not fair to tell users that they cannot implement something that is such a common and repeating pattern in the core.

Protocol requirements with default (no-op) implementations already satisfy that design goal, no?

-Chris

···

On Mar 31, 2016, at 11:49 AM, Yuval Tal via swift-evolution <swift-evolution@swift.org> wrote:

On Thu, Mar 31, 2016 at 2:23 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

on Wed Mar 30 2016, Yuval Tal <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

> Hi,
>
> I find that optional protocol methods to be very useful. However,
> there is a caveat -- it needs to be mapped to @objc. This puts a set
> of limitations, such as: structures cannot be used as parameters as it
> does not map to objective-c. What do you think about removing the
> requirement of using @objc and allow to create optional methods
> without these limitations?

Caveat: this is going to be strongly-worded; sorry in advance. I think
(no offense intended) it's a terrible idea. The whole notion of an
“optional requirement” is nonsensical to begin with, and the use of
optional protocol requirements encourages a style of programming that
lifts the responsibility of the protocol designer for careful design at
the expense of clients of the protocol. There are better ways to do
things; let's not propagate this anti-pattern any further than it's
already gone.

--
Dave

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto: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


(Matthew Johnson) #14

+1

We need to support optional methods in @objc protocols because it is required for interoperability. However, there are in my experience only reasons for an optional protocol method:

- There is a very common default behavior for a delegate (such as return immediately with a value of 0/nil/false) which would be a burden to require everyone to implement. We can instead accomplish this with protocol extensions

- The absence of a method in a delegate indicates a behavior that is otherwise impossible for the delegate to implement. The easiest example of this would be tableView:heightForRowAtIndexPath:, where the absence triggers an optimization where UITableView.rowHeight is used for all math, and its presence means that performance will be degraded, and the delegate will be called repeatedly for every single index path in order to figure out the table positioning/scroll height. I believe this has been shown to be much better solved via an optional additional protocol conformance to indicate that you are supporting a different ‘class’ of behavior, in this case that might be something like UITableViewVaribleHeightDelegate.

+1. I am not fully pleased with the design of the UITableViewDelegate and UITableViewDataSource protocols and this is one point that is problematic. It makes it impossible to implement a generic delegate that might sometimes need to have variable row height, but in other cases should take the fast path.

···

On Mar 31, 2016, at 5:51 PM, David Waite via swift-evolution <swift-evolution@swift.org> wrote:

I’d hate for there to be a push for optional protocol methods just because it can be confusing which methods are required in swift protocol implementations (vs defaulted by a protocol extension).

-DW

On Mar 31, 2016, at 12:23 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:

on Wed Mar 30 2016, Yuval Tal <swift-evolution@swift.org> wrote:

Hi,

I find that optional protocol methods to be very useful. However,
there is a caveat -- it needs to be mapped to @objc. This puts a set
of limitations, such as: structures cannot be used as parameters as it
does not map to objective-c. What do you think about removing the
requirement of using @objc and allow to create optional methods
without these limitations?

Caveat: this is going to be strongly-worded; sorry in advance. I think
(no offense intended) it's a terrible idea. The whole notion of an
“optional requirement” is nonsensical to begin with, and the use of
optional protocol requirements encourages a style of programming that
lifts the responsibility of the protocol designer for careful design at
the expense of clients of the protocol. There are better ways to do
things; let's not propagate this anti-pattern any further than it's
already gone.

--
Dave

_______________________________________________
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


(Guillaume Lessard) #15

They do, but that is not clear for everyone. And after protocol extensions appeared with 2.0, it took a little while to make that leap...

Documentation-wise, maybe the current “Optional Protocol Requirements” section should be moved from “The Swift Programming Language” to “Using Swift with Cocoa and Objective-C”. It would make it clearer that the feature exists for Objective-C interop. At the very least, moving that section to after the “Protocol Extensions" section would at least present the more important feature first.

Cheers,
Guillaume Lessard

···

On 31 mars 2016, at 22:01, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

Protocol requirements with default (no-op) implementations already satisfy that design goal, no?


(Sean Heber) #16

None taken. However, most of the delegate concept of UIKit relies heavily on this "nonsensical" requirement. It is impossible for someone to implement a control in swift which is "in the spirit" of UIKit, meaning the control has a delegate, with several methods that share the same name with different parameters, some are required and some are optional. I think it is not fair to tell users that they cannot implement something that is such a common and repeating pattern in the core.

Protocol requirements with default (no-op) implementations already satisfy that design goal, no?

-Chris

I agree that’s the case, but what happens for situations like UIKit where I do not have access to the source? If there is a protocol extension for, say, UITableViewDelegate that implements default behaviors, that extension’s existence might be publicly declared somewhere so I could know, but the implementation bodies would not be, correct? So as a user of UITableViewDelegate and without access to UIKit’s source code, I cannot examine the protocol extension to see what the extension is *actually* doing by default. The situation is then no better than it is now where the information about the default behavior or result of a missing delegate function is buried somewhere in the documentation. If the protocol spec itself included the “effective” default and there was no way to provide a default implementation that did anything *except* return a value (or do nothing), then the protocol declaration itself documents the intent and I, as an external user of UITableViewDelegate without access to the source code, could see exactly what it does.

Perhaps my understanding/reasoning there is flawed, but that's why I continue to suggest something like this:

protocol MyProtocol {
  default func semiOptionalFunctionA() -> Int = 42 // if unspecified, compiler generates implementation that returns 42
  default func semiOptionalFunctionB(thing: Int) // if unspecified, compiler generates implementation that does nothing
  func normalRequiredFunction() // requires an implementation by the user
}

l8r
Sean


(Brent Royal-Gordon) #17

Protocol requirements with default (no-op) implementations already satisfy that design goal, no?

Kind of. If I may steelman* optional members for a moment...

In cases where a default implementation would do, the default implementation will usually also be the behavior you want for a nil instance, but there's no convenient way to share logic between the two. For example, consider this:

  protocol UITableViewDelegate {
    ...
    func tableView(_ tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
  }
  extension UITableViewDelegate {
    func tableView(_ tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
      return tableView.rowHeight
    }
  }
  
  class UITableView {
    ...
    private func addRow(at indexPath: NSIndexPath) {
      ...
      cell.size.height = delegate?.tableView(self, heightForRowAtIndexPath: indexPath) ?? rowHeight
      ...
    }
    ...

You have to duplicate the default logic both in the default implementation and at the call site, but there is no convenient way to share it—the extension method can't call into an expression at some call site, and contrarily the call site can't invoke the default logic from the extension.

If the method were optional, then optional chaining would solve this problem for us:

  protocol UITableViewDelegate {
    ...
    optional func tableView(_ tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
  }
  
  class UITableView {
    ...
    private func addRow(at indexPath: NSIndexPath) {
      ...
      cell.size.height = delegate?.tableView?(self, heightForRowAtIndexPath: indexPath) ?? rowHeight
      ...
    }
    ...

This way, there is only one source of default behavior: the call site.

I'm also concerned by the thought of just how many sub-protocols we might end up with. When I try to fully factor NSTableViewDelegate (as it currently exists in the headers), I end up with ten protocols:

  NSTableViewDelegate
    - tableView:willDisplayCell:forTableColumn:row:

  NSTableViewLayoutDelegate: NSTableViewDelegate
    - tableView:heightOfRow:

  NSTableViewRowSelectionDelegate: NSTableViewDelegate
    - tableView:shouldSelectRow:
    - selectionShouldChangeInTableView:
    - tableViewSelectionIsChanging:
    - tableViewSelectionDidChange:
    - tableView:shouldTrackCell:forTableColumn:row: (10.5)
    - tableView:selectionIndexesForProposedSelection: (10.5)

  NSTableViewTypeSelectDelegate: NSTableViewDelegate (10.5)
    - tableView:typeSelectStringForTableColumn:row:
    - tableView:nextTypeSelectMatchFromRow:toRow:forString:
    - tableView:shouldTypeSelectForEvent:withCurrentSearchString:

  NSTableViewToolTipDelegate: NSTableViewDelegate
    - tableView:toolTipForCell:rect:tableColumn:row:mouseLocation:

  NSTableViewColumnDelegate: NSTableViewDelegate
    - tableView:shouldEditTableColumn:row:
    - tableView:shouldSelectTableColumn:
    - tableView:mouseDownInHeaderOfTableColumn:
    - tableView:didClickTableColumn:
    - tableView:didDragTableColumn:
    - tableViewColumnDidMove:
    - tableViewColumnDidResize:
    - tableView:sizeToFitWidthOfColumn: (10.6)
    - tableView:shouldReorderColumn:toColumn: (10.6)

  NSTableViewCellExpansionDelegate: NSTableViewDelegate (10.5)
    - tableView:shouldShowCellExpansionForTableColumn:row:
  
  NSTableViewCustomCellDelegate: NSTableViewDelegate (10.5)
    - tableView:dataCellForTableColumn:row:
    - tableView:isGroupRow:

  NSTableViewCellViewDelegate: NSTableViewDelegate (10.7)
    - tableView:viewForTableColumn:row:

  NSTableViewRowViewDelegate: NSTableViewDelegate (10.7)
    - tableView:rowViewForRow:
    - tableView:didAddRowView:forRow:
    - tableView:didRemoveRowView:forRow:
    - tableView:rowActionsForRow:edge: (10.11)

Some of these are probably unnecessary; they could be merged into NSTableViewDelegate and given default implementations. But at least a few of them would be very much needed. Would users be able to navigate this mess? Would they discover the features tucked away in sub-protocols? I'm just not sure.

And of course the safety issues that make optional protocol members dangerous in Objective-C don't exist in Swift. Swift will force you to test for the presence of an optional member; you can't carelessly call one.

(Incidentally, resilience might also benefit from supporting optional protocol members and adding a `public(optional)` feature which made all call sites outside the resilience domain treat all members as optional. You could then mark protocols meant to be called only by clients inside the resilience domain—like data sources and delegates—with `public(optional)` and gain the ability to delete obsolete members.)

* Steelmanning is the opposite of strawmanning.

···

--
Brent Royal-Gordon
Architechies


(Andrey Tarantsov) #18

Protocol requirements with default (no-op) implementations already satisfy that design goal, no?

Chris, as we've discussed in a thread that I think got forked from this one:

Yes, they do technically, but it would be nice to both:

1) make it an obvious documented part of the signature, possibly including the default return value

2) possibly make it less verbose by getting rid of the explicitly spelled out protocol extension

A.


(Rob Mayoff) #19

        class UITableView {
                ...
                private func addRow(at indexPath: NSIndexPath) {
                        ...
                        cell.size.height = delegate?.tableView(self,
heightForRowAtIndexPath: indexPath) ?? rowHeight
                        ...
                }
                ...

You need not duplicate the default logic:

private class DefaultDelegate: NSObject, UITableViewDelegate { }
private let defaultDelegate = DefaultDelegate()

public class UITableView {

    private func addRow(at indexPath: NSIndexPath) {
        ...
        cell.size.height = (delegate ?? defaultDelegate).tableView(self,
heightForRowAtIndexPath: indexPath)
        ...
    }

}


(TJ Usiyan) #20

-1
I hope that default implementations living in the protocol will address
this. I would even prefer to move in 'the other direction' and have
optional methods on protocols come into swift as default implementations.
TJ

···

On Sat, Apr 2, 2016 at 6:07 AM, Brent Royal-Gordon via swift-evolution < swift-evolution@swift.org> wrote:

> Protocol requirements with default (no-op) implementations already
satisfy that design goal, no?

Kind of. If I may steelman* optional members for a moment...

In cases where a default implementation would do, the default
implementation will usually also be the behavior you want for a nil
instance, but there's no convenient way to share logic between the two. For
example, consider this:

        protocol UITableViewDelegate {
                ...
                func tableView(_ tableView: UITableView,
heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
        }
        extension UITableViewDelegate {
                func tableView(_ tableView: UITableView,
heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
                        return tableView.rowHeight
                }
        }

        class UITableView {
                ...
                private func addRow(at indexPath: NSIndexPath) {
                        ...
                        cell.size.height = delegate?.tableView(self,
heightForRowAtIndexPath: indexPath) ?? rowHeight
                        ...
                }
                ...

You have to duplicate the default logic both in the default implementation
and at the call site, but there is no convenient way to share it—the
extension method can't call into an expression at some call site, and
contrarily the call site can't invoke the default logic from the extension.

If the method were optional, then optional chaining would solve this
problem for us:

        protocol UITableViewDelegate {
                ...
                optional func tableView(_ tableView: UITableView,
heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
        }

        class UITableView {
                ...
                private func addRow(at indexPath: NSIndexPath) {
                        ...
                        cell.size.height = delegate?.tableView?(self,
heightForRowAtIndexPath: indexPath) ?? rowHeight
                        ...
                }
                ...

This way, there is only one source of default behavior: the call site.

I'm also concerned by the thought of just how many sub-protocols we might
end up with. When I try to fully factor NSTableViewDelegate (as it
currently exists in the headers), I end up with ten protocols:

        NSTableViewDelegate
                - tableView:willDisplayCell:forTableColumn:row:

        NSTableViewLayoutDelegate: NSTableViewDelegate
                - tableView:heightOfRow:

        NSTableViewRowSelectionDelegate: NSTableViewDelegate
                - tableView:shouldSelectRow:
                - selectionShouldChangeInTableView:
                - tableViewSelectionIsChanging:
                - tableViewSelectionDidChange:
                - tableView:shouldTrackCell:forTableColumn:row: (10.5)
                - tableView:selectionIndexesForProposedSelection: (10.5)

        NSTableViewTypeSelectDelegate: NSTableViewDelegate (10.5)
                - tableView:typeSelectStringForTableColumn:row:
                - tableView:nextTypeSelectMatchFromRow:toRow:forString:
                -
tableView:shouldTypeSelectForEvent:withCurrentSearchString:

        NSTableViewToolTipDelegate: NSTableViewDelegate
                -
tableView:toolTipForCell:rect:tableColumn:row:mouseLocation:

        NSTableViewColumnDelegate: NSTableViewDelegate
                - tableView:shouldEditTableColumn:row:
                - tableView:shouldSelectTableColumn:
                - tableView:mouseDownInHeaderOfTableColumn:
                - tableView:didClickTableColumn:
                - tableView:didDragTableColumn:
                - tableViewColumnDidMove:
                - tableViewColumnDidResize:
                - tableView:sizeToFitWidthOfColumn: (10.6)
                - tableView:shouldReorderColumn:toColumn: (10.6)

        NSTableViewCellExpansionDelegate: NSTableViewDelegate (10.5)
                - tableView:shouldShowCellExpansionForTableColumn:row:

        NSTableViewCustomCellDelegate: NSTableViewDelegate (10.5)
                - tableView:dataCellForTableColumn:row:
                - tableView:isGroupRow:

        NSTableViewCellViewDelegate: NSTableViewDelegate (10.7)
                - tableView:viewForTableColumn:row:

        NSTableViewRowViewDelegate: NSTableViewDelegate (10.7)
                - tableView:rowViewForRow:
                - tableView:didAddRowView:forRow:
                - tableView:didRemoveRowView:forRow:
                - tableView:rowActionsForRow:edge: (10.11)

Some of these are probably unnecessary; they could be merged into
NSTableViewDelegate and given default implementations. But at least a few
of them would be very much needed. Would users be able to navigate this
mess? Would they discover the features tucked away in sub-protocols? I'm
just not sure.

And of course the safety issues that make optional protocol members
dangerous in Objective-C don't exist in Swift. Swift will force you to test
for the presence of an optional member; you can't carelessly call one.

(Incidentally, resilience might also benefit from supporting optional
protocol members and adding a `public(optional)` feature which made all
call sites outside the resilience domain treat all members as optional. You
could then mark protocols meant to be called only by clients inside the
resilience domain—like data sources and delegates—with `public(optional)`
and gain the ability to delete obsolete members.)

* Steelmanning is the opposite of strawmanning.

--
Brent Royal-Gordon
Architechies

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