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

Dave Abrahams dabrahams at apple.com
Thu Feb 4 13:10:45 CST 2016


> On Feb 4, 2016, at 10:58 AM, Dave Abrahams <dabrahams at apple.com> wrote:
> 
>> 
>> On Feb 4, 2016, at 12:23 AM, Radosław Pietruszewski <radexpl at gmail.com> wrote:
>> 
>> 
>> 
>>> On 04 Feb 2016, at 02:20, Dave Abrahams via swift-evolution <swift-evolution at swift.org> wrote:
>>> 
>>> 
>>> on Wed Feb 03 2016, Radosław Pietruszewski <swift-evolution at swift.org> wrote:
>>> 
>>>> 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.
>>> 
>>> For better or worse, it is a requirement that Cocoa as imported very
>>> closely approximates full conformance to the guidelines we choose.  We
>>> are shooting for consistency across APIs used in swift.
>> 
>> Hmm. Understood, but sigh. Personally I think it’s a shame to constrain ourselves to (imho) inferior naming convention because it’s what existing ObjC APIs use. Definitely understandable, but considering this isn’t a _really_ super common pattern, I think it would be just fine to leave the translations in good-enough state (whatever the decision on SE-0005 is), and follow a new convention in new Swift APIs.
> 
> Well, to give you an idea, we've been going with the ground rule that the number of APIs that need to be fixed up with NS_SWIFT_NAME because they become actively worse needs to be <1%.  I estimate that we can accept maybe 3% of APIs looking not-quite-right but no worse than before.
> 
> If these fall into that 3%, it might be acceptable.
> 
>>>> 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
>>> 
>>> That doesn't seem obvious to me.  Whether that "should" be in the base
>>> name depends on what guidelines we choose.  
>> 
>> Of course. The way I think of it: the primary semantic is “find a track”, and media type is a criterion for search. I understand method families aren’t something you’re looking to optimize, but you can imagine that _if_ this was a family, it would still be “find a track”, just with different criteria.
> 
> Yes, this makes a lot of sense to me.  Maybe we can distinguish the "find" cases from the "get" cases as you suggest below.
> 
>>>> - this breaks the symmetry with other methods due to the reason above
>>>> (like the “newTrack” you mentioned yourself) 
>>> 
>>> Yes, it would be more consistent if these two cases were the same.  
>>> 
>>>> - doesn’t play well with method families (searching for tracks is
>>>> searching for tracks. the criteria for search are just parameters).
>>> 
>>> I don't really believe that “method families” are something we want to
>>> optimize for in Swift.  There are almost always alternatives that impose
>>> lower cognitive overhead on users.
>> 
>> Fair.
>> 
>>> 
>>>> If we do
>>>> 
>>>> trackFactory.newTrack(mediaType: "Wax Cylinder")   // yes
>>>> 
>>>> I don’t see why it’s OK to do
>>>> 
>>>> a.tracksHavingMediaType("Wax Cylinder")      // yes
>>> 
>>> That's just the consistency argument again, right?
>> 
>> 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.
>>> 
>>> Unfortunately, this is the reality:
>>> 
>>> 1. The pattern of omitting prefix verbs like “get” and “find” is
>>> something of a sacred cow; I think it would be very hard to sell to
>>> certain important people.
>> 
>> Hmm, I didn’t think of that. “get” is sort of understandable, I can see how in some languages you’d do “getFoo()”, whereas in Swift it would probably be a property “foo”. I don’t see a problem with “find”, though, as it really does help convey the intent.
> 
> I'm not sure I know how to make that case without getting into arguments about efficiency.  Some people have a *very* strong resistance to allowing naming guidelines to be dependent on efficiency.  Ideas?

Maybe something about the possibility of not finding anything is the way to distinguish these.

> 
>>> 
>>> 2. if we were to standardize on the opposite, we would need an
>>> objective-C import strategy that would add these verbs automatically.
>>> 
>>> If you can get a handle on solving #2, it *might* be worth me taking a
>>> shot at solving #1. Otherwise, I'm afraid this idea is dead in the
>>> water.  Nothing that leaves glaring inconsistencies between imported
>>> Cocoa and the API guidelines is going to be acceptable.
>> 
>> Then perhaps let’s meet halfway:
>> 
>>  a.tracksWith(mediaType: “BetaMax”)
>>  a.removeFirstTrackWith(mediaType: “BetaMax”)
>>  fac.newTrack(mediaType: “Wax Cyllinder”)
>> 
>> (replace “with” with “matching” or “having” if you prefer)
>> 
>> I don’t love it, but like it much more than “tracksHavingMediaType”:
>> 
>> - still much more consistent about what goes into explicit parameters
>> - having a “with”/etc helps convey that parameters are the criteria for tracks returned
> 
> I can't disagree.  At least one of us in the guidelines working group has a visceral negative reaction to ending a basename with "With".  I'll have to ask whether that applies to every preposition or not.
> 
>> - doable as an automatic translation from ObjC
> 
> Hmm, are you sure?  The problem may be identifying those places where "with" functions this way vs. as a vacuous separator. The latest best heuristic we've got for doing that is to consider it a vacuous separator when there's a verb in the name, but that will fall down on removeFirstTrackWith...
> It's all about finding an automated approximation to the actual guidelines that gives a fairly low error rate.
> 
>>>> 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)
>>> 
>>> There are contexts in which "matching" is more ambiguous than "having",
>>> e.g.
>>> 
>>>  x.trackMatchingMediaType(t) // track the matching media type?
>>> 
>>>  x.trackHavingMediaType(t)   // track is obviously a non-verb here.
>>> 
>>> Yes, I see how this relates to your "put back the verb" idea.
>>> 
>>>> 
>>>> 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
>>>> 
>>>> _______________________________________________
>>>> swift-evolution mailing list
>>>> swift-evolution at swift.org
>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>> 
>>> -- 
>>> -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