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

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

on Tue Feb 02 2016, David Owens II <swift-evolution at swift.org> wrote:

> Awesome for putting this together. I think the rules add some needed
> clarity, but they have the bias that the first argument *should* have
> a defaulted empty parameter label. 

I disagree with this as a premise.  IMO they don't have any such bias.
The guideline says, only under these very specific conditions does one
omit an argument label.  That would indicate, if anything, a bias
towards *having* a label.

Since you used the phrase “a defaulted empty parameter label,” it sounds
like you're talking about language rules for deciding whether there's an
argument label based on what you write in the declaration.  I *really*
don't want to discuss that in this thread.

Between the two points above, I don't know whether to try to analyze
what you wrote below.  Have I misunderstood?  Please advise.

> Instead, if the premise was that the first argument is treated no
> differently then other arguments, then the rule becomes simply a list
> of exceptions on when the label is not necessary:
> If the argument's purpose in the primary semantics is clear from the
> name of the function, then no argument label is required (regardless
> of argument position).
> To be clear, this rule does not change the API guidelines statement
> that all labelled arguments should be placed after non-labelled
> arguments.
> Examples:
> Remove Label(s)
> a.contains(b) // the question of containment of b in a is the primary
> semantic
> a.merging(b)   // combining b with a is the primary semantic
> a.moveTo(300, 400) // moving to coordinates is a primary semantic of a
>                     // the labels can be removed when a has a clear
> semantic
>                     // limitation of a two-pair coordinate system
> a.readFrom(u, ofType: b) // u is clearly the target of the primary
> semantic
>                           // "reading", ofType labeled as it's a
> modifier
> Keep Label(s) - the default behavior
> a.dismiss(animated: b)  // dismissal is the primary semantic;
>                         // `animated` is a modifier to that semantic,
> so
>                         // keep the label
> a.moveTo(x: 300, y: 400) // moving is the primary semantic, however a
>                           // has no clear semantic limitation to a 2D
>                           // coordinate context so keep the labels
> a.read(from: u, ofType: b) // keep from as there is no non-ambiguous
> semantic
>                             // relationship between u and what is
> being read
>                             // without the label. e.g. without from,
> it's not
>                             // clear that read is reading from u, it
> could read
>                             // from stdin.
> The `moveTo` example is also an illustration of why `max` would have
> no labels:
> max(x, y)  // maximum value is primary semantic of the parameter set
> I put the `a.readFrom(_:ofType:)` and `a.read(from:ofType:)` as an
> illustration of two valid API names that would have different
> applications of the rule depending on choice of the first part of the
> function identifier. I'll argue below which I think is better, in the
> general case, and the modification of rule #2 to support it.
>> 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]
>> 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
> I see this rule as more about how to name the APIs instead of being
> about the argument labels. This is also strikes at the root of our
> fundamental disagreement that we cannot seem to resolve. =)
> You seem to treat the entire function identifier to two disjoint parts
> (please correct me if I'm misunderstanding you): the base name,
> including the relationship to the first parameter, and the supporting
> labels. The base name is the primary indicator of what the function
> does, with the inclusion of how the first argument relates to that
> semantic. The remaining argument labels are there mostly as secondary
> information.
> a.tracksHavingMediaType("Wax Cylinder")
> // identifier: tracksHavingMediaType(_:)
> However, I treat the entire identifier as a complete whole with no
> real deference to the base name over the argument labels. So the above
> example would be broken down like this:
> a.tracksHaving(mediaType: "Wax Cylinder")
> // identifier: tracksHaving(mediaType:)
> **IF** we start with the premise that argument labels are required by
> default, the simplified ruleset I used above already answers how to
> put the label on regardless of which "base name" you chose.
> I would then change rule #2 to this: the "base name" of the function
> describes the semantic intention while the argument labels are used to
> describe their relationship to that semantic intention or modification
> of how that intent is to be carried out.
> a.tracksHavingMediaType("Wax Cylinder") // no, `MediaType` relates to
> the argument, not
>                                            // primary intent of the
> function.
> a.tracksHaving(mediaType: "Wax Cylinder") // yes, the primary intent
> is to find tracks
>                                            // based on some criteria.
> No special rules or exceptions are necessary.
>> 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
> The labelling piece is already covered by the simplified rule #1 above
> and by my modified rule #2, so this rule is
> redundant. `WithIdentifier` and `WithName` should never be present
> because they belong to the first parameter, not to the semantic
> intention of the function.
> The choice between `transitionToScene(_:)`, `transitionTo(_:)`, or
> even `transitionTo(scene:)` is an interesting one. I do not think
> there is an objectively clear winner based on the limited sample
> context. Though if we followed the more direct guidance of the
> modified rule #2, the only real options left would be:
> `transitionTo(_:)` and `transitionTo(scene:)`.
> If `a` were a scene manager and the only transitioning that would take
> place, it could be argued that `transitionTo(_:)` is enough.
> If `a` were a general transitioning engine, then
> `transitionTo(scene:)` would be the choice based on the modified rules
> #1 and #2 as the semantic intention of transitioning is clear, but the
> target would not be, so the `scene` label would be kept.
> For clarity: `transitionToScene(_:)` would not be a candidate as rule
> #2 states that qualifiers for arguments, in this case `Scene`, belong
> with the argument itself.
> In the very least, I hope this better articulates why I don't prefer
> the treatment of the first argument as special, and why I do not put
> first argument information into the "base name" of the function.
> Cheers!
> -David
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution


More information about the swift-evolution mailing list