[swift-evolution] [Proposal] Make optional protocol methods first class citizens
T.J. Usiyan
griotspeak at gmail.com
Fri Apr 1 19:55:23 CDT 2016
-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 at 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 at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160402/eb37e291/attachment.html>
More information about the swift-evolution
mailing list