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

Radosław Pietruszewski radexpl at gmail.com
Wed Feb 3 11:14:03 CST 2016


Overall, great guidelines (and +1 to the rules Erica wrote up), and I’m +1 on conveying these nuances in the guidelines.

> 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

The rationale for doing this is stronger when we talk about automatic translation of Objective-C APIs.

But in APIs designed for Swift, I feel like this is wrong IMHO, because:

- “media type” is still a parameter, so it shouldn’t be in the base name itself
- this breaks the symmetry with other methods due to the reason above (like the “newTrack” you mentioned yourself)
- doesn’t play well with method families (searching for tracks is searching for tracks. the criteria for search are just parameters).

If we do

   trackFactory.newTrack(mediaType: "Wax Cylinder")   // yes

I don’t see why it’s OK to do

   a.tracksHavingMediaType("Wax Cylinder")      // yes

Of course just “tracks” is confusing, and we agree on that, but I would strongly recommend that for new APIs we don’t just name the method with a word of an already-existing instance, rather, we start it with a verb:

a.findTracks(mediaType: “BetaMax”) // or “searchTracks”, or alternatively “tracksMatching"
a.removeFirstTrackMatching(mediaType: “BetaMax”)   — ad 2
fac.newTrack(mediaType: “Wax Cylinder”)

Symmetric, predictable, follows the same convention, plays well with method families (i.e. different search criterion than media type), and no clarity problems.

Ad 2: I can see why you don’t like “removeFirstTrack”. It sounds like removing _the_ first track, rather than the first track that matches criteria in parameters list. Perhaps a word like “Matching” would work well to fix this concern. (And sounds/conveys intention better than “with” or “having” IMHO)

Just my 2¢,
— Radek

> On 03 Feb 2016, at 01:32, Dave Abrahams via swift-evolution <swift-evolution at swift.org> wrote:
> 
> 
> This thread is related to the review of new API guidelines, but it's not
> a review thread; it's exploratory.  The goal is to come up with
> guidelines that:
> 
> * describe when and where to use argument labels
> * require labels in many of the cases people have asked for them
> * are understandable by humans
> * preserve important semantics communicated by existing APIs.
> 
> 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:
> 
>     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
> 
>   [Note that this covers all the direct object cases and, I believe,
>   all the default argument cases too, so maybe that exception can be
>   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.
> 
> * 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.
> 
> 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
> 
> 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
> 
> Thoughts?
> 
> -- 
> -Dave
> 
> _______________________________________________
> 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