[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