[swift-evolution] When to use argument labels (a new approach)

Dave Abrahams dabrahams at apple.com
Wed Feb 3 16:22:16 CST 2016


on Tue Feb 02 2016, Paul Cantrell <swift-evolution at swift.org> wrote:

> +1 for 1, with an asterisk
> mixed feelings about 2
> +1 for 3
>
> Notes inline below.
>
>> On Feb 2, 2016, at 6:32 PM, Dave Abrahams via swift-evolution
>> <swift-evolution at swift.org> wrote:
>> 
>> Here's what I'm thinking
>> 
>> 1. If and only if the first argument could complete a sentence*
>>   beginning in the base name and describing the primary semantics of
>>   the call, it gets no argument label:
>
> Yes. The thoughts of “sentence” and “primary semantics” succinctly
> capture a lot of good ideas from previous discussion.
>
>>     a.contains(b)  // b completes the phrase "a contains b"
>>     a.mergeWith(b) // b completes the phrase "merge with b"
>> 
>>     a.dismiss(animated: b) // "a, dismiss b" is a sentence but 
>>                            // doesn't describe the semantics at all, 
>>                            // thus we add a label for b.
>> 
>>     a.moveTo(x: 300, y: 400) // "a, move to 300" is a sentence 
>>                              // but doesn't describe the primary 
>>                              // semantics, which are to move in both
>>                              // x and y.  Thus, x gets a label.
>> 
>>     a.readFrom(u, ofType: b) // "a, read from u" describes
>>                              // the primary semantics, so u gets no
>>                              // label. b is an
>>                              // option that tunes the primary
>>                              // semantics
>
> All these examples seem totally solid.
>
>>   [Note that this covers all the direct object cases and, I believe,
>>   all the default argument cases too, so maybe that exception can be
>
> Not sure about this one in theory … but yes, I can see how it would
> play out that way in practice.

This is important; we want to pick rules that are as simple as possible
and still play out the right way in practice.  I will be raising that
point again in this thread.

>>   dropped.  We still need the exceptions for full-width type
>>   conversions and indistinguishable peers]
>> 
>>   Note: when there is a noun in the base name describing the role of the
>>   first argument, we skip it in considering this criterion:
>> 
>>      a.addObserver(b) // "a, add b" completes a sentence describing 
>>                       // the semantics.  "Observer" is omitted in 
>>                       // making this determination.
>
> Not sure you need this complicating exception. “a, add observer b” is still a sentence.

Um, okay, I hadn't looked at it that way.  But if the variable wasn't b but
some more complicated name or expression, you probably wouldn't think
that it makes a sentence e.g.

  a.addObserver(backgroundController)

We could either say you're allowed to mentally transform that into

  a.addTheObserverIndicatedBy(backgroundController) 

(don't forget that can be an arbitrary expression, so you really need
something like “IndicatedBy”, and not just “Called”) or we can say
you're allowed to skip over “Observer.”  I think the latter is simpler.

>> * We could say "clause" here but I think making it an *independent*
>>  clause doesn't rule out any important use-cases (see
>>  https://web.cn.edu/kwheeler/gram_clauses_n_phrases.html) and at that
>>  point, you might as well say "sentence," which is a more
>>  universally-understood term.
>
> Yes.
>
>> 2. Words that describe attributes of an *already-existing* instance
>>   should go in the base name rather than in a label:
>> 
>>      a.tracksHavingMediaType("Wax Cylinder")      // yes
>>      a.removeFirstTrackHavingMediaType("BetaMax") // yes
>> 
>>      a.tracks(mediaType: "Wax Cylinder")          // no
>>      a.removeFirstTrack(havingMediaType: "BetaMax") // no
>> 
>>   [yes, we could use "With" instead of "Having", but it's more
>>   ambiguous]
>
> I have reservations about this. It doesn’t generalize perfectly well:
>
> 	a.tracksHavingMediaType("Wax Cylinder”, andPianist: “Brahms”)
>
> …strikes me as awkward compared to one of these:
>
> 	a.tracksHaving(mediaType: "Wax Cylinder”, pianist: “Brahms”)
> 	// or 
> 	a.tracksMatchingCriteria(mediaType: "Wax Cylinder”, pianist: “Brahms”)
> 	// or even
> 	a.tracks(mediaType: "Wax Cylinder”, pianist: “Brahms”)

Yes, we can extend the guideline to add the distribution pattern used in
the first example above (and mentioned in my reply to Charles
Kissinger).  The latter two are more ambiguous than the first one, FWIW;
I can think of misreadings for both of those.

> …especially since, in a method of that form, _all_ the arguments are
> likely to have a default value of nil:
>
> 	a.tracks(mediaType: "Wax Cylinder”)
> 	a.tracks(pianist: “Brahms”)

Ah... you make an interesting point here.  If it was reasonable to leave
out all the parameters, you'd have to allow

    a.tracksHaving()

and likely someone will want a “tracks” property to go with this.  I
know one way to disallow the above line without exorbitant cost to users
of the API, which is to build an API that supports this:

    a.tracksWhere(.pianist == "Brahms" && .mediaType == "Wax Cylinder")

or 

    a.tracksWhere(.pianist == "Brahms", .mediaType == "Wax Cylinder")

That's doable in a library, but by no means simple to code.  So here I
think we're just bumping up against the limits of what the language allows
us to express easily.  I think we'll have to declare the "a.tracksHaving()"
problem out of scope for the naming convention discussion.

Since I've rambled afar, please let me know if I've addressed your
concern here.

>>   Words that describe attributes of an instance *to be created* should
>>   go in argument labels, rather than the base name (for parity with
>>   initializers):
>> 
>>      AudioTrack(mediaType: "BetaMax")                   // initializer
>>      trackFactory.newTrack(mediaType: "Wax Cylinder")   // yes
>> 
>>      trackFactory.newTrackWithMediaType("Wax Cylinder") // no
>
> Yes, definitely.
>
>> 3. (this one is separable) When the first argument is the *name* or
>>   *identifier* of the subject in the base name, do not label it or
>>   describe it in the base name.
>> 
>>      a.transitionToScene(.GreatHall)               // yes
>>      a.transitionToSceneWithIdentifier(.GreatHall) // no
>> 
>>      let p = someFont.glyph("propellor")           // yes
>>      let p = someFont.glyphWithName("propellor")   // no
>>      let p = someFont.glyph(name: "propellor")     // no
>
> Yes, seems solid.
>
> Cheers, P
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

-- 
-Dave



More information about the swift-evolution mailing list