[swift-evolution] Delegate Method Conventions (was:[Review] SE-0023 API Design Guidelines)

plx plxswift at icloud.com
Tue Jan 26 08:40:47 CST 2016

> On Jan 24, 2016, at 5:57 PM, Dave Abrahams via swift-evolution <swift-evolution at swift.org> wrote:
> on Sat Jan 23 2016, plx <swift-evolution at swift.org> wrote:
>>> On Jan 23, 2016, at 2:33 PM, Dave Abrahams via swift-evolution
>>> <swift-evolution at swift.org> wrote:
>>> on Sat Jan 23 2016, plx
>>> <swift-evolution at swift.org
>>> <mailto:swift-evolution at swift.org>> wrote:
>>>>> On Jan 22, 2016, at 6:12 PM, Ross O'Brien via swift-evolution
>>>>> <swift-evolution at swift.org> wrote:
>>>>> How would we apply this to delegate patterns?
>>>>> For example, would we keep
>>>>> tableview(tableView:cellForRowAtIndexPath:), or would we switch to
>>>>> delegate(tableView:cellForRowAtIndexPath:) ?
>>>>> Or perhaps better, for clarity over which protocol is being
>>>>> conformed to / which property of the delegator is calling the
>>>>> function:
>>>>> dataSource(tableView:cellForRowAtIndexPath:),
>>>>> delegate(tableView:didSelectRowAtIndexPath:)
>>>> FWIW, I am personally favorable to a more radical-renaming for delegate methods, roughly the below:
>>>> func numberOfSections(inTableView tableView: UITableView) -> Int // <- against guidelines, but symmetric
>>>> func numberOfRows(inTableView tableView: UITableView, forSection section: Int) -> Int
>>>> func cellForRow(inTableView tableView: UITableView, atIndexPath indexPath: NSIndexPath) -> UITableView
>>> The interesting thing about delegate methods is that, for the most part,
>>> use-sites don't appear in user code.  So *if* you're going to come up with
>>> special conventions just for delegate methods you'd want to serve the
>>> declaration site.  I don't know what these things *ought* to look like,
>>> but the declarations above look to me like they've got an awful lot of
>>> redundancy that doesn't help readability.
>> Most of what follows should really be in the discussion about the
>> Objective-C import, not here, but I’ll respond here with the parts
>> relevant to the guidelines.
>> It seems self-evident that imported delegate methods violate the
>> spirit of Swift’s API guidelines; in particular, the rule that
>> “Methods can share a base name when they share the same basic meaning
>> but operate on different types, or are in different domains” seems
>> relevant. 
> That's quite true.
>> It’s thus been a bit surprising to me that delegate-style methods
>> haven’t *already* gotten some special treatment; 
> Well, it's a fact of life that major efforts like this one (probably
> property behaviors are the same bucket) are going to have to land
> without solving all the problems they are related to.  I believe
> strongly that we should do *something* about delegate methods.  I also
> believe they're a separable problem and we should be able to evaluate
> the current direction without working out all the details of how we're
> going to handle them.  That's why I changed the subject line: I'd like
> to agree that special treatment for delegate methods in the importer is
> out-of-scope in this review.
>> what I had isn’t great, but put it and some variants up against the
>> original, like so:
>> func numberOfRows(in tableView: UITableView, forSection section: Int) -> Int
>> func numberOfRowsIn(tableView: UITableView, forSection section: Int) -> Int
>> func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
>> func numberOfRows(inTableView tableView: UITableView, forSection section: Int) -> Int
> I assume you mean the 3rd one to be "the original?”

Yes, here: tableView(_:numberOfRowsInSection:) <https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableViewDataSource_Protocol/#//apple_ref/occ/intfm/UITableViewDataSource/tableView:numberOfRowsInSection:>
>> …(note the longest is only ~10 characters longer than the shortest!). 
> Sorry, I don't see why that is relevant.  Care to explain?

I did not make the intention clear; apologies. I was intending to illustrate that although all of the examples contain redundancies, none of them are egregiously worse than the others (including the original); the worst case is only moderately more-redundant than the best case.

>> Although there might be an as-yet unseen option that’s superior to all
>> of the above, just out of those 4 it’s hard to see how you can justify
>> option #3 using the API guidelines; 
>> it also seems hard to envision a self-consistent expansion of the
>> guidelines that’d lead to favoring #3.
> You can't.
>> As already noted this is really more-relevant to the “objective-c
>> import revision”, but you can frame my points as obliquely asking “to
>> what extent should the Swift API guidelines actually matter when doing
>> the big Objective-C import?”
> We're willing to accept that some imported APIs will not follow the
> guidelines.
>> I also question your sense of real-world use of delegate protocols;
>> just taking inventory of the most recent project I completed, it looks
>> like it had 5 custom delegate-style protocols. Of these, 4 had exactly
>> one implementation each, and 1 had exactly 2 implementations; 
> And how many use-sites were there?

5, just counting “classes using said delegates”; 14 if you go by individual method use. 

>> I don’t think this is that untypical. If you accept it as not too
>> atypical,
> I do.
>> it suggests a more uniform balance between defining a delegate
>> protocol, using said protocol, and implementing said protocol.
> Not necessarily.  How many times did this project implement delegate
> protocols that were defined elsewhere?  

Looks like 12, for implementations; “a lot”, going by by the method count.

In any case I don’t dispute the general point, just perhaps the exit.

> In any case, for what it's worth, I personally think the direction
> you're going with those delegate APIs is great, and it has the benefit
> of bringing them into conformance with other guidelines.  My only point
> in saying that the declaration site is more important with delegate
> methods than with others is that there's more type information at the
> declaration site of a method than at its use site, so there's definitely
> no reason to make them more verbose than others.  Making them simply
> follow the existing guidelines exactly is a simple solution that IMO
> leads to good code, and one I would support.
> However, what Cocoa guys like Tony Parker say about the eventual
> direction of delegate APIs should probably carry a lot more weight than
> what I say.
>> To wind this digression down now, the API guidelines’ attitude towards
>> redundancy seems somewhat troubling; no one wants needless redundancy,
>> but natural languages tend towards redundancy (cf
>> agreement/pleonasm/etc) and it’s not at all self-evident that less
>> redundancy always implies increased readability (which you may or may
>> not be intending to imply; I can’t tell)…especially when it’s easy to
>> get fooled by increased speed-of-reading.
> This seems like a pretty vague concern.  Let's see concrete examples of
> problems you think the guidelines' attitude toward redundancy will
> cause.  FWIW, "omit needless words" isn't something we just came
> up with ourselves: it's a time-honored principle of clear English
> writing (google it).

Sure, sure, but if you’ll forgive a cheap shot I’d point out Strunk would’ve tut-tutted here and suggested, perhaps, “we didn’t invent ‘omit needless words’”. The tricky part of that rule is that what’s needless is highly contextual, and to be *understood* when writing in a highly-condensed style usually requires a large amount of shared context.

Which need-for-context is at the root of my admittedly-vague concern; I’ve done my best to come up with a concrete-ish example, but it’s a bit contrived and not as strong as I’d like, either. It’s more of an "ecosystem concern”, too.

Here are *six* functions that could conceivably be named `min` under the guidelines:

func min() -> Generator.Element? // obviously only where `Generator.Element` is `Comparable`
func min(isLessThan comparator: (Generator.Element,Generator.Element) -> Bool) -> Generator.Element?
func min<K:Comparable>(extractor: (Generator.Element) -> K) -> K?
func min<K:Comparable>(extractor: (Generator.Element) -> K?) -> K?
func min<T>(extractor: (Generator.Element) -> T, isLessThan comparator: (T,T) -> Bool) -> T?
func min<T>(extractor: (Generator.Element) -> T?, isLessThan comparator: (T,T) -> Bool) -> T?

…and perhaps they *all* should be named `min` (and we simply let context and type information sort it all out for us).

But if the names should be different, what’re good choices?

My vague concern is that having “maximally-terse” names for the standard library functions makes it trickier to choose "non-misleading” names for such closely-related variants.

EG: if you go with `minValue` for the variants, to a casual reader there’s room for confusion vis-a-vis `min` (I suspect many would initially guess that `minValue` does what `minElement` does today, but would guess the behavior correctly if given a choice between `minElement` and `minValue`).

Unfortunately for my case, I think `minFor` is a perfectly-reasonable choice here, which undermines my concrete example (I warned you the case wasn’t going to be very convincing).

But that’s the kind of vague concern I have here: that a “maximally-terse” naming convention can be harder to extend in a way that’s both self-consistent and not-potentially-misleading. 

But I don’t have a great suggestion for an additional guideline, and there may be nothing serious to worry about here, either.

> -- 
> -Dave
> _______________________________________________
> 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/20160126/ba42ae0a/attachment.html>

More information about the swift-evolution mailing list