<div dir="ltr"><br><div class="gmail_extra"><br><div class="gmail_quote">On Tue, Feb 2, 2016 at 4:32 PM, Dave Abrahams via swift-evolution <span dir="ltr">&lt;<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><br>
This thread is related to the review of new API guidelines, but it&#39;s not<br>
a review thread; it&#39;s exploratory.  The goal is to come up with<br>
guidelines that:<br>
<br>
* describe when and where to use argument labels<br>
* require labels in many of the cases people have asked for them<br>
* are understandable by humans<br>
* preserve important semantics communicated by existing APIs.<br>
<br>
Here&#39;s what I&#39;m thinking<br>
<br>
1. If and only if the first argument could complete a sentence*<br>
   beginning in the base name and describing the primary semantics of<br>
   the call, it gets no argument label:<br>
<br>
     a.contains(b)  // b completes the phrase &quot;a contains b&quot;<br>
     a.mergeWith(b) // b completes the phrase &quot;merge with b&quot;<br>
<br>
     a.dismiss(animated: b) // &quot;a, dismiss b&quot; is a sentence but<br>
                            // doesn&#39;t describe the semantics at all,<br>
                            // thus we add a label for b.<br>
<br>
     a.moveTo(x: 300, y: 400) // &quot;a, move to 300&quot; is a sentence<br>
                              // but doesn&#39;t describe the primary<br>
                              // semantics, which are to move in both<br>
                              // x and y.  Thus, x gets a label.<br>
<br>
     a.readFrom(u, ofType: b) // &quot;a, read from u&quot; describes<br>
                              // the primary semantics, so u gets no<br>
                              // label. b is an<br>
                              // option that tunes the primary<br>
                              // semantics<br>
<br>
   [Note that this covers all the direct object cases and, I believe,<br>
   all the default argument cases too, so maybe that exception can be<br>
   dropped.  We still need the exceptions for full-width type<br>
   conversions and indistinguishable peers]<br>
<br>
   Note: when there is a noun in the base name describing the role of the<br>
   first argument, we skip it in considering this criterion:<br>
<br>
      a.addObserver(b) // &quot;a, add b&quot; completes a sentence describing<br>
                       // the semantics.  &quot;Observer&quot; is omitted in<br>
                       // making this determination.<br>
<br>
* We could say &quot;clause&quot; here but I think making it an *independent*<br>
  clause doesn&#39;t rule out any important use-cases (see<br>
  <a href="https://web.cn.edu/kwheeler/gram_clauses_n_phrases.html" rel="noreferrer" target="_blank">https://web.cn.edu/kwheeler/gram_clauses_n_phrases.html</a>) and at that<br>
  point, you might as well say &quot;sentence,&quot; which is a more<br>
  universally-understood term.<br>
<br></blockquote><div><br></div><div>+1</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">
2. Words that describe attributes of an *already-existing* instance<br>
   should go in the base name rather than in a label:<br>
<br>
      a.tracksHavingMediaType(&quot;Wax Cylinder&quot;)      // yes<br>
      a.removeFirstTrackHavingMediaType(&quot;BetaMax&quot;) // yes<br>
<br>
      a.tracks(mediaType: &quot;Wax Cylinder&quot;)          // no<br>
      a.removeFirstTrack(havingMediaType: &quot;BetaMax&quot;) // no<br>
<br>
   [yes, we could use &quot;With&quot; instead of &quot;Having&quot;, but it&#39;s more<br>
   ambiguous]<br>
<br>
   Words that describe attributes of an instance *to be created* should<br>
   go in argument labels, rather than the base name (for parity with<br>
   initializers):<br>
<br>
      AudioTrack(mediaType: &quot;BetaMax&quot;)                   // initializer<br>
      trackFactory.newTrack(mediaType: &quot;Wax Cylinder&quot;)   // yes<br>
<br>
      trackFactory.newTrackWithMediaType(&quot;Wax Cylinder&quot;) // no<br>
<br></blockquote><div><br></div><div>Very mixed feelings on this, probably adding up to a +0.2 or so.  I&#39;ll mention a couple concerns that I haven&#39;t seen anyone raise:</div><div><br></div><div>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):</div><div><br></div><div>  let ops = [</div><div>    self.removeFirstTrackHavingMediaType,</div><div>    self.addTrackWithMediaType</div><div>    self.populateTrackOperationsForMediaType</div><div>    self.play</div><div>  ]</div><div><br></div><div>vs.</div><div><br></div><div>  let ops = [</div><div>    self.removeFirstTrack</div><div>    self.addTrack</div><div>    self.populateTrackOperations</div><div>    self.play</div><div>  ]</div><div><br></div><div>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&#39;t be able to disambiguate them:</div><div><br></div><div>  // Compile error: Invalid redeclaration of removeFirstTrack</div><div>  func removeFirstTrack(havingMediaType: String) { ... }</div><div>  func removeFirstTrack(named: String) { ... }</div><div>  func removeFirstTrack(byArtist: String) { ... }</div><div><div><br class="">  // Compile error only when the function is referenced</div><div>  func removeTrack(n: Int, named: String)</div><div>  func removeTrack(n: Int, byArtist: String)</div><div>  let f = self.removeTrack   // named: or byArtist:?</div></div><div><br></div><div>  // Legal...</div><div>  func removeFirstTrackHavingMediaType(_: String) { ... }</div><div>  func removeFirstTrackNamed(_: String) { ... }</div><div>  func removeFirstTrackByArtist(_: String) { ... }</div><div><br></div><div>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.</div><div><br></div><div>My other reservation is the flip side of this argument: when you use a function in a first-class context, often you *want* to leave things unspecified.  After all, the whole point of parameterized code is so that it can be used in more contexts.  This comes most often with containers or other generic functions:</div><div><br></div><div>  myStringArray.map(myString.appendContentsOf)    // wordy and awkward</div><div>  myStringArray.map(myString.append)  // cleaner</div><div><br></div><div>I&#39;d consider this a less severe problem, but something I&#39;d still like considered, particularly given the existence of several higher-order functions in the standard library.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">
3. (this one is separable) When the first argument is the *name* or<br>
   *identifier* of the subject in the base name, do not label it or<br>
   describe it in the base name.<br>
<br>
      a.transitionToScene(.GreatHall)               // yes<br>
      a.transitionToSceneWithIdentifier(.GreatHall) // no<br>
<br>
      let p = someFont.glyph(&quot;propellor&quot;)           // yes<br>
      let p = someFont.glyphWithName(&quot;propellor&quot;)   // no<br>
      let p = someFont.glyph(name: &quot;propellor&quot;)     // no<br>
<br><br></blockquote><div><br></div><div>+1 </div></div><br></div></div>