[swift-evolution] Delegate Method Conventions
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
>>>>> 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
>> 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:)
>>> …(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
>>> 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
>> 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
> 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
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
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
More information about the swift-evolution