[swift-evolution] Delegate Method Conventions

Dave Abrahams dabrahams at apple.com
Tue Jan 26 18:34:00 CST 2016


on Tue Jan 26 2016, plx <swift-evolution at swift.org> wrote:

>> 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.

I understand that, but if you'll forgive me for being blunt, so what?

>>> 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.

My point is that with most methods, the use sites clearly overwhelm the
declaration sites.  I don't believe that to be the case with delegates.
But this is really a minor detail.

>> 
>>> 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.

Oh, that *was* cheap; and I'm not sure I can forgive it! :-)

> 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.

We are not going for maximal terseness in the standard library.  When we
pick a name like "min" it's because of precedent and expectations.
>
> 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).

It is just as you said :-)

> 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.

I think we've made it very clear that we're not going for maximal
terseness:

   Clarity is more important than brevity. Although Swift code can be
   compact, it is a non-goal to enable the smallest possible code with
   the fewest characters. Brevity in Swift code, where it occurs, is a
   side-effect of the strong type system and features that naturally
   reduce boilerplate.

-- 
-Dave



More information about the swift-evolution mailing list