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

Jonathan Tang jonathan.d.tang at gmail.com
Wed Feb 3 15:22:07 CST 2016


On Wed, Feb 3, 2016 at 1:17 PM, Douglas Gregor <dgregor at apple.com> wrote:

>
> On Feb 3, 2016, at 1:10 PM, Jonathan Tang <jonathan.d.tang at gmail.com>
> wrote:
>
>
>
> On Wed, Feb 3, 2016 at 9:44 AM, Douglas Gregor <dgregor at apple.com> wrote:
>
>>
>> On Feb 2, 2016, at 10:17 PM, Jonathan Tang via swift-evolution <
>> swift-evolution at swift.org> wrote:
>>
>>
>>
>> On Tue, Feb 2, 2016 at 4:32 PM, Dave Abrahams via swift-evolution <
>> swift-evolution at swift.org> wrote:
>>
>>>
>>> 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
>>>
>>>
>> Very mixed feelings on this, probably adding up to a +0.2 or so.  I'll
>> mention a couple concerns that I haven't seen anyone raise:
>>
>> Please consider the first-class function case when naming.  Particularly
>> since Swift encourages passing functions around as objects rather than
>> using string selectors.  #2 implies that the prepositional phrase will
>> appear when *referencing* the method (vs. calling it):
>>
>>   let ops = [
>>     self.removeFirstTrackHavingMediaType,
>>     self.addTrackWithMediaType
>>     self.populateTrackOperationsForMediaType
>>     self.play
>>   ]
>>
>> vs.
>>
>>   let ops = [
>>     self.removeFirstTrack
>>     self.addTrack
>>     self.populateTrackOperations
>>     self.play
>>   ]
>>
>> The second option wins on verbosity, but the first arguably gives more
>> clarity as to what the methods actually do.  Also, the second has a
>> potentially annoying semantic problem: if you have overloads for these
>> methods that differ only in keyword, Swift won't be able to disambiguate
>> them:
>>
>>   // Compile error: Invalid redeclaration of removeFirstTrack
>>   func removeFirstTrack(havingMediaType: String) { ... }
>>   func removeFirstTrack(named: String) { ... }
>>   func removeFirstTrack(byArtist: String) { ... }
>>
>>   // Compile error only when the function is referenced
>>   func removeTrack(n: Int, named: String)
>>   func removeTrack(n: Int, byArtist: String)
>>   let f = self.removeTrack   // named: or byArtist:?
>>
>>   // Legal...
>>   func removeFirstTrackHavingMediaType(_: String) { ... }
>>   func removeFirstTrackNamed(_: String) { ... }
>>   func removeFirstTrackByArtist(_: String) { ... }
>>
>> Unless the Swift compiler were to change to make the former set legal, I
>> think this is a powerful argument in favor of this proposal, because
>> otherwise you may find that the compiler prevents you from writing the code
>> that the guidelines encourage.  You might also find that changing the type
>> of an overload means you have to change the name to prevent a collision,
>> which could be very surprising to users.
>>
>>
>> I suspect that you missed SE-0021:
>>
>>
>> https://github.com/apple/swift-evolution/blob/master/proposals/0021-generalized-naming.md
>>
>> which lets you name the arguments in a function reference, e.g:
>>
>> let f = self.removeTrack(_:named:)
>> let f2 = self.removeTrack(_:byArtist:)
>>
>> let ops = [
>>   self.removeFirstTrack(mediaType:),
>>   self.addTrack(mediaType:),
>>   self.populateTrackOperationsForMediaType,
>>   self.play
>> ]
>> - Doug
>>
>>
>>
>
> There's still no outstanding proposal to that lets you disambiguate
> functions only by the first argument at the point of definition, right?
>
>   class C {
>     // Still a compiler error: "Invalid redeclaration of doSomething"
>     func doSomething(withBar: String) -> String { return "Bar\(withBar)" }
>     func doSomething(withFoo: String) -> String { return "Foo\(withFoo)" }
>   }
>
> Without this, I think there's still a big problem with moving the
> preposition to the first argument label, because it leads to recommended
> names that don't compile.
>
>
> You’re tripping over the default behavior. If you want two different
> functions, doSomething(withBar:) and doSomething(withFoo:), then you need
> to specifically provide an argument label for each:
>
> class C {
>   func doSomething(withBar bar: String) -> String { return “Bar\(withBar)”
> }
>   func doSomething(withFoo foo: String) -> String { return “Foo\(withFoo)”
> }
> }
>
> Per SE-0021, you can reference these with “C.doSomething(withBar:)” and
> “C.doSomething(withFoo:)”, respectively.
>
> With SE-0021 and the ability to define overloads that differ only in the
> first-arg label (they'd have to be called or referenced with the label,
> otherwise you'd get a compiler error at point of use) I think I'd move to
> about -0.5 on proposal #2, because:
>
> 1. Moving the preposition to the first argument adds brevity in the common
> case where there is no overload conflict.
> 2. You can still disambiguate or add clarity at the point of reference by
> including all the argument labels
> 3. It's more uniform with how you'd reference multi-arg functions.
>
>
> Given the above clarification, does this opinion still hold?
>
>
>
Thanks, yes it does.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160203/39a68e15/attachment.html>


More information about the swift-evolution mailing list