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

Dave Abrahams dabrahams at apple.com
Wed Feb 3 18:52:05 CST 2016


on Wed Feb 03 2016, Dave Abrahams <swift-evolution at swift.org> wrote:

> on Tue Feb 02 2016, Erica Sadun <swift-evolution at swift.org> wrote:
>
>> Thoughts? Thoughts:
>>
>> Swift prizes clarity. Its parameter labeling system emphasizes
>> self-documentation and guides code production. In nearly every case,
>> labels follow three simple rules:
>>
>> Skip argument labels for a method or function's first parameter
>> Use argument labels for a method or function's subsequent parameters
>> Require argument labels for initializers
>>
>> These base rules enhance Swift legibility. Unlike other languages
>> whose positional argument names have meaning only within the
>> implementation context, Swift's labels convey use and meaning at the
>> calling site. This creates better communication, enhances
>> maintainability, and adheres to the principle that code is written
>> rarely and read and reviewed often.
>>
>> At times, special circumstances may apply to your code as explored in
>> the following rules:
>>
>> Skip first argument labels when the first argument completes a sentence established in the base name. If the argument describes a call's primary semantics, it does not require a label:
>>     a.contains(b)  // b completes the phrase "a contains b"
>>     a.mergeWith(b) // b completes the phrase "merge with b"
>>     a.readFrom(u, ofType: b) // "a, read from u" describes
>>                              // primary semantics so u gets no
>>                              // label. 
>>                              // b is an option that tunes the 
>>                              // primary semantics
>> Skip the first argument label when a noun in the base name describes the first argument's role.
>>    a.addObserver(b) // "add b" completes a meaningful sentence that
>>                     // defines the intentended semantics.  The first
>>                     // argument is the "Observer".
>> Move the first argument label to the base name when it describes a name or identifier that acts as the subject of the base action.
>>      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
>> Move the first argument label to the base name when it describes argument attributes of existing instances.
>>      a.tracksOfMediaType("Wax Cylinder")      // yes
>>      a.removeFirstTrackOfMediaType("BetaMax") // yes
>>
>>      a.tracks(mediaType: "Wax Cylinder")            // no
>>      a.removeFirstTrack(havingMediaType: "BetaMax") // no
>> Use first label arguments when the first parameter is semantically distinct from the base name and does not complete a meaningful "sentence"
>>     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.
>> Use all argument labels when the relationship between arguments is semantically stronger than the relationship between the first argument and the base name.
>>     moveTo(x: a, y: b)
>>     login(userName: a, password: b)
>>     constructColor(red: r, green: g, blue: b, alpha: a)
>> Omit labels for argument peers that cannot be usefully distinguished.
>>     min(number1, number2)
>>     zip(sequence1, sequence2)
>> Use explicit argument labels to describe attributes of an instance that's being created. Your calls should resemble initializers.
>>      AudioTrack(mediaType: "BetaMax")                   // initializer
>>      trackFactory.newTrack(mediaType: "Wax Cylinder")   // yes
>>
>>      trackFactory.newTrackOfMediaType("Wax Cylinder")   // no
>> Use first argument labels that would have normally appeared in the base name when building groups of related calls whose implementations are distinguished specifically by their parameters. Your calls should resemble initializers. 
>>   login(userName: a, password: b) // not loginWithUserName(a, password: b)
>>   login(credential: a) // not loginWithCredential(a)
>> Skip first argument labels for initializers when using full width type conversions, that is when initializing from instances of another type.
>>  extension String { 
>>      // Convert `x` into its textual representation 
>>      // in the given radix
>>      init(_ x: BigInt, radix: Int = 10) 
>>  }
>>  text = "The value is: "
>>  text += String(veryLargeNumber)
>>  text += " and in hexadecimal, it's"
>>  text += String(veryLargeNumber, radix: 16)
>> Use first argument labels when narrowing initial values to make it conform to restrictions within the new type. The label should describe how the instance will be modified:
>>  extension UInt32 {
>>      init(_ value: Int16) // Widening, so no label 
>>      init(truncating bits: UInt64)
>>      init(saturating value: UInt64)
>>  }
>
> Okay... since you didn't directly address anything I wrote, should I
> take this to mean you think I got it all wrong?  Or...?

OK, sorry, that was hasty; you rewrote what I wrote.  But again, I'm
missing any explanation of what you think your writeup accomplishes that
mine does not.  Is it an improvement?  If so, why and how?

-- 
-Dave



More information about the swift-evolution mailing list