When to use argument labels (a new approach)

To do a complete evaluation also need to think about
what applying the guidelines will do to your own code.

You’re right! I extracted the list of method definitions from my app and looked through it.

The overwhelming majority of my methods either take no arguments, or
start with a verb and make a phrase where the first parameter doesn’t
require a label (rule B1).

Some methods I noticed (I don’t know if this is useful for you, but if
it is then here you go):

  private func setHandoffState(hash hash: String, fallbackURL: String, title: String? = nil, searchable: Bool) {

This seems OK to me, but I’m not entirely sure if the guidelines
suggest I _should_ put a preposition there, i.e.

  setHandoffStateFor(hash: a, fallbackURL: b, …)

I don’t think I should — I don’t see how “for” adds to clarity here,

Me neither, but it's hard to tell what this method is supposed to mean.
Maybe you could explain (or show a beautifully crafted doc comment
(BCDC) per
<Swift.org - API Design Guidelines;
;-)

but I want to make sure if I interpret the proposed guidelines right.

  func actionForCommand(command: UIKeyCommand) -> String? {

This should become “actionFor(command: c)” — which isn’t an obvious

That looks like a call, in which case the API above doesn't allow an
argument label. (I think this kind of confusion is probably the
strongest reason for changing the default---I see it come up over and
over).

win, but I certainly don’t mind it.

according to where we're currently headed (prepositions inside the
parens), it would be:

    func action(for command: UIKeyCommand) -> String? {

  private func createAttribute(tag: SecItemAttr, _ data: NSString?) -> SecKeychainAttribute? {

Forgive my barbaric removal of external labels.

Nit: “argument labels,” please.

It should be `createAttribute(tag: t, data: d)`.

Again that looks like a call, so for the above declaration it would be

      createAttribute(t, d)

Alternatively, it could be `attributeFor(tag: t, data: d)` — I could
go either way.

Why “for?” Can you explain the semantics of this method (or show a BCDC)?

(adding to my thoughts on the later proposal of moving preposition
inside parens, the symmetry in param labels would likely be broken
here, because it doesn’t seem necessary to add “for”/“with” when I go
with “createAttribute”, but for the noun-only version it would have to
be “attribute(forTag: t, data: d)”.)

  func attachmentForImage(image: UIImage) throws -> Attachment {
  func attachmentForData(data: NSData) -> Attachment {
  func attachmentForFileURL(url: NSURL) throws -> Attachment {

This should be `attachmentFor(image:)`, `attachmentFor(data:)`, and
`attachmentFor(fileURL:)`.

again, the latest draft puts prepositions inside the parens. But
without the BCDC it's pretty hard to tell whether these are good names.

This seems like a win, since related methods for different data types
are grouped more tightly together. (I could also — like you suggested
about not optimizing for method families — make an enum, but it would
be overkill since it’s not API for public consumption. I should mark
those as private.)

I'm not trying to say “method families are bad.” I'm just saying I
don't want to build the guidelines around them if possible.

  private func messageCell(label label: String) -> MessageCell {
  private func cellForTask(task: Task, enabled: Bool = true) -> TaskCell {

Hm, those are inconsistent. I could go with `cellFor(message:)` and

don't you mean cellFor(label:) ?

···

on Thu Feb 11 2016, Radosław Pietruszewski <swift-evolution@swift.org> wrote:

`cellFor(task:)`, which would be consistent with the guidelines. OTOH,
since they return different types, I liked that they had different
names… `taskCellFor(task:)` is redundant. Perhaps `taskCellFor(_:)`
and `messageCellFor(_:)`.

  func generate(fill: Double, text: String) -> CLKComplicationTemplate {

`templateFor(fill:, text:)`

  func update(from old: TaskCommentRowModel?, to new: TaskCommentRowModel) {

That’s the, IMO rare, circumstance where the preposition *does* make sense to go inside the parens.

* * *

I mentioned this in some other post before, but I also found a bunch
of methods where the first parameter should be labeled, but isn’t,
because without an easy shortcut like the old #param, I was just too
lazy to do The Right Thing.

PS. If someone wanted to do the same thing and review methods in their
project, here’s the quick&dirty Ruby script for this:
Extract function declarations from your Swift project. Just open up `irb` from your project run and paste this: · GitHub
<https://gist.github.com/radex/b425c73afde84d88e4ca&gt;

HTH,
— Radek

On 08 Feb 2016, at 21:55, Dave Abrahams via swift-evolution >> <swift-evolution@swift.org> wrote:

on Mon Feb 08 2016, Radosław Pietruszewski <swift-evolution@swift.org> wrote:

Dave,

First of all, thank you for enduring our nitpicks and complaints and
continuing to explore the subject :) I think we’re all better off for
it, and getting closer to the solution with each iteration.

You asked:

1. I'm not expecting these guidelines to make everybody optimally happy,
all the time, but they shouldn't be harmful. Are there any cases for
which they produce results you couldn't live with?

And I think, by this standard, the guidelines you proposed seem to be
a success. Looking through Doug’s diffs, I see a lot of method names
that I don’t *love*, but I couldn’t find something I would hate.

That's great news! Keep in mind, though, that Doug's results are just
the result of trying to approximate application of the guidelines by
using heuristics. To do a complete evaluation also need to think about
what applying the guidelines will do to your own code.

Thanks,

--
-Dave

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

--
-Dave

Hello,

PS. If someone wanted to do the same thing and review methods in their project, here’s the quick&dirty Ruby script for this: Extract function declarations from your Swift project. Just open up `irb` from your project run and paste this: · GitHub

Thanks, it’s much useful.

  func attachmentForImage(image: UIImage) throws -> Attachment {
  func attachmentForData(data: NSData) -> Attachment {
  func attachmentForFileURL(url: NSURL) throws -> Attachment {

This should be `attachmentFor(image:)`, `attachmentFor(data:)`, and `attachmentFor(fileURL:)`. This seems like a win, since related methods for different data types are grouped more tightly together. (I could also — like you suggested about not optimizing for method families — make an enum, but it would be overkill since it’s not API for public consumption. I should mark those as private.)

If such convention were to be adopted, we could throw away our favorite grep tools.

Functions with many parameters are often more legible when their invocation is split across several lines. And generally speaking, a developer can liberally call a function on a single line, or on several lines:

  attachmentFor(image: …, extraParam: …)
  attachmentFor(
      image: …,
      extraParam: …)

OK so now if I want to look for all invocations of attachmentFor(image:extraParam:) in my code, I have to look for "attachmentFor", and get all the unrelated results attachmentFor(data:…), attachmentFor(fileURL:…), etc.

Whereas if the function were named attachmentForImage(_:extraParam:), I could look for "attachmentForImage", and get much more precise search results.

So… I’m happy people discuss how nice `attachmentFor(image:)` looks, and my opinion on how nice or ugly it looks is not my point. My point is that I want my tools to help me doing my job.

Gwendal

···

Le 11 févr. 2016 à 12:34, Radosław Pietruszewski via swift-evolution <swift-evolution@swift.org> a écrit :

  private func setHandoffState(hash hash: String, fallbackURL: String, title: String? = nil, searchable: Bool) {

This seems OK to me, but I’m not entirely sure if the guidelines
suggest I _should_ put a preposition there, i.e.

  setHandoffStateFor(hash: a, fallbackURL: b, …)

I don’t think I should — I don’t see how “for” adds to clarity here,

Me neither, but it's hard to tell what this method is supposed to mean.
Maybe you could explain (or show a beautifully crafted doc comment
(BCDC) per
<Swift.org - API Design Guidelines;
;-)

Ahh, but I’m missing BCDCs in many of these places ;)

This is a helper that essentially makes a NSUserActivity from passed parameters and calls `becomeCurrent` on it. (Pretty much changes application-global state.) (Not the most elegant solution, I know.)

but I want to make sure if I interpret the proposed guidelines right.

  func actionForCommand(command: UIKeyCommand) -> String? {

This should become “actionFor(command: c)” — which isn’t an obvious

That looks like a call, in which case the API above doesn't allow an
argument label.

I’m sorry — I got confused — what do you mean here?

(I think this kind of confusion is probably the
strongest reason for changing the default---I see it come up over and
over).

win, but I certainly don’t mind it.

according to where we're currently headed (prepositions inside the
parens), it would be:

   func action(for command: UIKeyCommand) -> String? {

  private func createAttribute(tag: SecItemAttr, _ data: NSString?) -> SecKeychainAttribute? {

Forgive my barbaric removal of external labels.

Nit: “argument labels,” please.

Noted.

It should be `createAttribute(tag: t, data: d)`.

Again that looks like a call, so for the above declaration it would be

     createAttribute(t, d)

Alternatively, it could be `attributeFor(tag: t, data: d)` — I could
go either way.

Why “for?” Can you explain the semantics of this method (or show a BCDC)?

This is a initializer-like helper method that instantiates a SecKeychainAttribute from passed parameters.

(adding to my thoughts on the later proposal of moving preposition
inside parens, the symmetry in param labels would likely be broken
here, because it doesn’t seem necessary to add “for”/“with” when I go
with “createAttribute”, but for the noun-only version it would have to
be “attribute(forTag: t, data: d)”.)

  func attachmentForImage(image: UIImage) throws -> Attachment {
  func attachmentForData(data: NSData) -> Attachment {
  func attachmentForFileURL(url: NSURL) throws -> Attachment {

This should be `attachmentFor(image:)`, `attachmentFor(data:)`, and
`attachmentFor(fileURL:)`.

again, the latest draft puts prepositions inside the parens.

Q: For the previous example you suggested the latest draft would be `action(for:)`. Given these methods take one of three different parameter types, and the names mostly just repeat type information, should they all be `attachment(for:)`? Or `attachment(forImage:)` etc? The third method is trickier because it takes an NSURL, but there is an assumption this is a _file_ (i.e. device-local) URL.

But
without the BCDC it's pretty hard to tell whether these are good names.

Also an initializer-like helper method that transforms passed parameters into an Attachment.

···

This seems like a win, since related methods for different data types
are grouped more tightly together. (I could also — like you suggested
about not optimizing for method families — make an enum, but it would
be overkill since it’s not API for public consumption. I should mark
those as private.)

I'm not trying to say “method families are bad.” I'm just saying I
don't want to build the guidelines around them if possible.

  private func messageCell(label label: String) -> MessageCell {
  private func cellForTask(task: Task, enabled: Bool = true) -> TaskCell {

Hm, those are inconsistent. I could go with `cellFor(message:)` and

don't you mean cellFor(label:) ?

`cellFor(task:)`, which would be consistent with the guidelines. OTOH,
since they return different types, I liked that they had different
names… `taskCellFor(task:)` is redundant. Perhaps `taskCellFor(_:)`
and `messageCellFor(_:)`.

  func generate(fill: Double, text: String) -> CLKComplicationTemplate {

`templateFor(fill:, text:)`

  func update(from old: TaskCommentRowModel?, to new: TaskCommentRowModel) {

That’s the, IMO rare, circumstance where the preposition *does* make sense to go inside the parens.

* * *

I mentioned this in some other post before, but I also found a bunch
of methods where the first parameter should be labeled, but isn’t,
because without an easy shortcut like the old #param, I was just too
lazy to do The Right Thing.

PS. If someone wanted to do the same thing and review methods in their
project, here’s the quick&dirty Ruby script for this:
Extract function declarations from your Swift project. Just open up `irb` from your project run and paste this: · GitHub
<https://gist.github.com/radex/b425c73afde84d88e4ca&gt;

HTH,
— Radek

On 08 Feb 2016, at 21:55, Dave Abrahams via swift-evolution >>> <swift-evolution@swift.org> wrote:

on Mon Feb 08 2016, Radosław Pietruszewski <swift-evolution@swift.org> wrote:

Dave,

First of all, thank you for enduring our nitpicks and complaints and
continuing to explore the subject :) I think we’re all better off for
it, and getting closer to the solution with each iteration.

You asked:

1. I'm not expecting these guidelines to make everybody optimally happy,
all the time, but they shouldn't be harmful. Are there any cases for
which they produce results you couldn't live with?

And I think, by this standard, the guidelines you proposed seem to be
a success. Looking through Doug’s diffs, I see a lot of method names
that I don’t *love*, but I couldn’t find something I would hate.

That's great news! Keep in mind, though, that Doug's results are just
the result of trying to approximate application of the guidelines by
using heuristics. To do a complete evaluation also need to think about
what applying the guidelines will do to your own code.

Thanks,

--
-Dave

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

--
-Dave

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

Thanks, yes it does.

···

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

On Feb 3, 2016, at 1:10 PM, Jonathan Tang <jonathan.d.tang@gmail.com> > wrote:

On Wed, Feb 3, 2016 at 9:44 AM, Douglas Gregor <dgregor@apple.com> wrote:

On Feb 2, 2016, at 10:17 PM, Jonathan Tang via swift-evolution < >> swift-evolution@swift.org> wrote:

On Tue, Feb 2, 2016 at 4:32 PM, Dave Abrahams via swift-evolution < >> swift-evolution@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?

That's a matter of opinion. I personally don't like subscripts that might take O(N) time, but Cocoa certainly has them <SKNode | Apple Developer Documentation.

(I was going to say I don't like subscripts that can themselves return a collection, but then I remembered slicing, which seems perfectly natural to me. So that one's withdrawn.)

Jordan

···

On Feb 3, 2016, at 15:42, Jessy Catterwaul via swift-evolution <swift-evolution@swift.org> wrote:

The last example is more appropriate as a named subscript.

tracks[mediaType: .WaxCylinder, pianist: "Brahms"]

On Feb 3, 2016, at 5:34 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

on Wed Feb 03 2016, Matt Whiteside <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I have one comment (below) about this part:

On Feb 2, 2016, at 18:59, Paul Cantrell via swift-evolution >>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I have reservations about this. It doesn’t generalize perfectly well:

  a.tracksHavingMediaType("Wax Cylinder”, andPianist: “Brahms”)

…strikes me as awkward compared to one of these:

  a.tracksHaving(mediaType: "Wax Cylinder”, pianist: “Brahms”)
  // or
  a.tracksMatchingCriteria(mediaType: "Wax Cylinder”, pianist: “Brahms”)
  // or even
  a.tracks(mediaType: "Wax Cylinder”, pianist: “Brahms”)

…especially since, in a method of that form, _all_ the arguments are
likely to have a default value of nil:

  a.tracks(mediaType: "Wax Cylinder”)
  a.tracks(pianist: “Brahms”)

I prefer the original:

a.tracksHavingMediaType("Wax Cylinder", andPianist: "Brahms”)

to any of these,

a.tracksHaving(mediaType: "Wax Cylinder", pianist: "Brahms")
// or
a.tracksMatchingCriteria(mediaType: "Wax Cylinder", pianist: "Brahms")
// or even
a.tracks(mediaType: "Wax Cylinder", pianist: "Brahms”)

Because to me, the 3 alternatives, especially the last one, all read
more like hashmap accesses than method calls. In other words, they
make it appear as if you are passing in arbitrary keys to be queried,
rather than calling one specific method.

I *think* I understand what you like and why you like it, but I can't
say I understand your rationale. To test my understanding, what do you
think of this:

     a.tracksHaving(mediaType: "Wax Cylinder", andPianist: "Brahms")

?

Thanks,

--
-Dave

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

See my response down below

I’m in agreement with what Ricardo is saying here:

I personally like it and I agree with all except the following:

let p = someFont.glyph("propellor")
let p = someFont.glyphWithName("propellor”)

To be more specific, this example looks good to me:
a.transitionToScene(.GreatHall) // yes

but this one:
let p = someFont.glyph("propellor") // yes

somehow doesn’t. Maybe because it reads more like subscripting a
dictionary. As a result, I think this second example kind of detracts
from this section of the guidelines.

I wonder if it looks better written like this?

let p = someFont.glyph("LATIN SMALL LETTER A WITH ACUTE")

"propellor" was the argument in an example of this method in use that I
could find on the web, but it's a really odd way to spell that word.
Maybe it has something to do with that.

I don’t think I’m understanding the difference.

If you don't see that it makes a difference, that answers my question.
It doesn't look any better to you.

At the risk of distracting you with minutiae, this particular example
feels more like a property access than a method call, so my mind wants
to rewrite it as this:

someFont.glyphs[“LATIN SMALL LETTER A WITH ACUTE”]

and that makes it a little harder to stay focused on the intent of the guideline.

Well, I agree with you in general. There are lots of methods in Cocoa like

      view.addGestureRecognizer(sumthin)

that would be so much better as

     view.gestureRecognizers.add(sumthin)

But since I doubt there's any way we're going to be able to
automatically make the conversion, or convert NSFont.glyphWithName into
a subscript access on a property, the question of how to write such
methods is still important.

···

on Wed Feb 03 2016, Matt Whiteside <mwhiteside.dev-AT-gmail.com> wrote:

On Feb 3, 2016, at 17:32, Dave Abrahams via swift-evolution >> <swift-evolution@swift.org> wrote:
on Wed Feb 03 2016, Matt Whiteside <swift-evolution@swift.org> wrote:

--
-Dave

What I was getting at there is that
‘a.tracksHavingMediaTypeWaxCylinder’ doesn’t seem like a sentence to
me. It is a clause, no doubt, but doesn’t seem to be an independent
one.

Agh, you're right. If only we used verbs in these cases... but that is
a whole nother discussion. I guess I'll have to say "clause" instead of
"sentence," which really sucks. Or I can say there's an implied "get"
at the front if there's no verb. bleah.

The implied ‘get' approach doesn’t seem too bad to me. Would it suffice to stick with ‘sentence' and add the condition that if there is an implied ‘get' or ‘set' that would form a sentence, then Rule 1 still applies?

I can’t think of any cases where people use an implied verb other than something in the 'get' family (find, acquire, etc.). Looking through my own code, the very concise set of rules in your original post work perfectly (IMO) with the exception of the “missing get” problem.

—CK

···

On Feb 3, 2016, at 3:54 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:
on Wed Feb 03 2016, Charles Kissinger <swift-evolution@swift.org> wrote:

It was unclear to me whether it didn’t fit the Rule 1 criteria for
that reason, but Rule 2 was specifically including it, or whether I
just wasn’t seeing the “sentence-like” nature of it.

—CK

—CK

On Feb 2, 2016, at 4:32 PM, Dave Abrahams via swift-evolution <swift-evolution@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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

--
-Dave

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

--
-Dave

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Why isn't "to" more predictable than "toPoint?" It's certainly more
universal.

Function arguments can be unordered, if they have default values. A
contrived example here...

func rename( from nameA="index", from extA="html", to nameB="converted", to
extB="shtml" )

Maybe I'm confused, but that seems like it has to be illegal at worst, and
confusing at best. But if you don't consistently use "from" and "to" then
you're back to making special rules about everything.

It wouldn't matter if every argument has a unique Type, or if you take the
time to create structs or tuples to bundle every piece of data together
about "from" or about "with" etc. Maybe that's usually the case, but it's
nice to have freedom to be lazy.

For the examples discussed below:

func indexOf(element: Element) -> Index?
and:
func rangeOf(searchString: String, options: NSStringCompareOptions) ->
NSRange

would conform to the guidelines given an “implied get” rule, would they not?

trimming() is the problem child.

—CK

···

On Feb 4, 2016, at 11:47 AM, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:

on Thu Feb 04 2016, plx <swift-evolution@swift.org> wrote:

On Feb 4, 2016, at 12:17 AM, Dave Abrahams via swift-evolution >> <swift-evolution@swift.org> wrote:

Okay, now I'm finally giving this its due—sorry, too much pressure
earlier in the day…

No need to apologize, you have a commendable dedication to replies.

Especially now that the disagreements are getting to be increasingly-minor.

on Wed Feb 03 2016, plx >> <swift-evolution@swift.org >> <mailto:swift-evolution@swift.org>> wrote:

After reading these guidelines and seeing the responses I am glad to
see some consideration given to argument labeling.

After thinking it over, I think the rules *I* would like to see can be
expressed very straightforwardly much more can be made much A minor
point, but I think it’s important to distinguish between
single-argument functions and multi-argument functions; doing so seems
to simplify the rules (even if there are more of them).

Everything that follows is my preferences, but I generally agree with
Erica’s suggestions in the concrete cases.

I'll have to go back and have a look at that
*goes back to have a look*

Are there cases that add something significant to what I've already
written? I didn't see any. If you're saying what I wrote can be
improved by covering specific examples, please tell me which ones.

I also think the emphasis on quasi-grammatical rules-and-roles is
something of a dead-end for design guidelines and won’t include such
considerations in what follows.

Characterizing what we've done in the proposed guidelines as emphasizing
“quasi-grammatical rules-and-roles” seems like an unnecessary and
slightly muddled potshot. The grammatical stuff in the current document
is not in any sense “quasi” and has no connection to the admonition to
clarify roles, which I consider to be important. That said, I'm not at
all attached to presenting the guidelines in terms of grammar, so I'm
happy to see how your approach works.

On a re-read your thread-starter has been toned-down enough from
before it’s a lot less in that direction, but see the main response
below.

## RULES

### I. Single-Argument Functions:

#### RULES:

- general rule: don’t label the first argument
- exceptions:
- (a) the argument has a default value (`removeAll(keepCapacity: Bool = default)`)
- (b) the function acts-like a constructor (covered in your rule 2)
- (c) the “ecosystem rule” (see section III)
- (d) the semantics of the argument are non-obvious (see below)

#### REMARKS:

I’m not sure (d) actually exists, though; every concrete example I can
think up either falls under rule (b) or rule (c). It may not actually
need to be a rule (other than as, perhaps, the underlying motivation
for rules (b) and (c)).
My intent with (d) was to address a similar concern as in Erica’s
`init(truncating …)` and `init(saturating …)`: “if a reasonable reader
would be unclear which of N plausible implementation choices

I think/hope you mean “semantic” rather than “implementation” here.

you are making, you may wish to label the argument, even if you only
have a single such function”…but, again, it’s hard to find any
examples for (d) that aren’t also some mixture of (b) and/or (c).

Okay, well the fewer (ahem) guidelines, the better. Let's pretend you
didn't propose (d) and see how well it works.

### II. Multi-Argument Functions:

#### RULES:

- general rule: label all arguments
- exceptions:
- (a) omit the first label whenever the first argument is the
  semantic focus, and the other arguments are some mix of “details,
  adjustments, or modifiers”

This seems to be a different way of expressing something I was getting
at with the guidelines I posted to start this thread. I worry that what
it means for an argument to be “the semantic focus” is too vague. Why
is it an improvement over what I wrote?

I address this in the main response.

- (b) omit labels entirely whenever argument-ordering is irrelevant
  to the output (see below)

I don't think you mean this case:

func f(answer answer: Int = 42, message: String = "Hello, world") {
print("\(message)! The answer is \(answer)")
}

Why is this an improvement over the way it's phrased in the original
guidelines proposal: “when the arguments are peers that can't be
usefully distinguished”?

My framing made more sense to me because “peers” and “can’t easily be
distinguished” seemed vaguer than what I *meant*, but based on the
counterexample you supplied my phrasing must indeed be too awful to
use.

I was trying to replace “peers that can’t usefully be distinguished”
(what are peers? what does “can’t be usefully distinguished” actually
mean?) with something stronger (perhaps "any permutation of arguments
produces indistinguishable results”), but that wouldn’t actually cover
all cases either (e.b. `isLessThan`).

We can always try to refine what I wrote to make it clearer, but in this
thread I'd like to focus on the *substance* of the guidelines for first
argument labels.

#### REMARKS:

For (a), the assumption is that we have a general consensus that “in
methods for which one of the arguments is the semantic focus, that
argument should be the first argument”; this seems pretty widely
followed.

I think using known and well-defined terms like “sentence” (or even
lesser-known but well-defined terms like “clause”) is probably much
better than using an ill-defined concept like “argument is the semantic
focus.” Even if you can define this concept clearly, it would have to
offer some very compelling advantages to be an improvement over
something that is already well-established. What are those?

This is the main reply.

First, I think it’s fair to say that, as-formulated, you can’t simultaneously:

- (a) take the sentence/clause rule seriously (or “use it rigorously”)
- (b) justify many of the existing standard-library APIs (and the intended Cocoa imports)

Consider the following examples:

// today:
func rangeOfString(searchString: String, options mask: NSStringCompareOptions) -> NSRange
func stringByTrimmingCharactersInSet(characterSet: NSCharacterSet) -> String
func indexOf(element: Element) -> Index?

// ideal tomorrow, i assume:
func rangeOf(searchString: String, options: NSStringCompareOptions) ->
NSRange

s.find(subString, .ignoringCase | .ignoringDiacriticalMarks) -> Range<String.Index>

func trimming(characterSet: NSCharacterSet) -> String func

s.trimming(.whitespaceAndNewlines)

Gah; you're right, here. Keeping a linguistic basis requires backing
off all the way from “sentence” to “phrase.” That's actually where I
started with this guideline, but thought I'd be able to strengthen it.
Saying “sentence or noun phrase” (if it works) might be better than just
“phrase” because “noun phrase” is always a term of art, whereas “phrase”
might just be interpreted casually.

indexOf(element: Element) -> Index?

Also a noun phrase.

…how do we (b) justify the lack of first-argument labels in that
“ideal tomorrow” while still also (a) actually applying the
sentence/clause rule?

Well, I think it's just that the rule needs a little adjusting.

At least to my eyes, “a, range of b”, “a, trimming b”, and “a, index
of b”, are questionable even as clauses, let alone as sentences.

Yes. Phrases, though?

It seems hard to resolve the above without either:

- introducing a *lot* of wiggle-room (“completes a sentence, or would
only require some vacuous filler words to complete a sentence”)
- complicating it tremendously (one rule for noun-like method names,
another for verb-like method names, another for -ing method names,
etc.)
- weakening the condition from an if-and-only-if *rule* to a “may
consider, but also remember other guidelines like ‘omit needless
words’"

…and thus although I generally agree with what I see as the *intent*
behind the sentence/clause rule, I’m not sure it’s actually a usable
rule as-formulated.

I agree. But I think minor adjustments fix it.

If you accept that the “sentence/clause rule” is gone, what’s left of
the proposed guideline is the “describes the primary semantics”
aspect.

Here, I prefer a formulation for “primary semantic focus” because it
seems it leads to easier dispute-resolution. EG, for `addObserver`, it
seems easier to get agreement that `playbackController` is somehow the
“focus” in uses like the below:

// ignoring the `options:context:` part:
playerItem.addObserver(playbackController, forKeyPath: “status”)
playerItem.addObserver(playbackController, forKeyPath: “duration”)
playerItem.addObserver(playbackController, forKeyPath: “tracks”)

I would argue that the receiver is the obvious focus, so this seems much
less clear to me.

…than to win an argument over whether or not “add observer” describes
*enough* of the “primary semantics” of this method to fall under the
proposed guideline.

That seems less ambiguous to me than primary focus, and I believe a few
examples in the guidelines would be enough to settle most debates.

But that’s just an opinion, and on reflection I don’t think “primary
semantic focus” is the clearest formulation, either; I just don’t have
anything better.

This rule seems to cover e.g. `addObserver(_:forKeyPath:)` and
`addObserver(_:selector:name:object:)` and `encodeObject(_:forKey:)`
and `present(_:animated:completion:)` (née
`presentViewController(_:animated:completion:)`), and so on.

A point to bring up is that under these rules, the “evolution” of a
name would be different: the just-so story for how
`addObserver(_:forKeyPath:)` came to be so-called is that it *began*
as `add(observer:forKeyPath:)`, but b/c the `observer` argument is the
semantic focus it "made sense to move `observer` into the method
name”; that is, the assumption is that functions like
`addObserver(_:forKeyPath:)` are considered to be exceptions to the
"base convention” and need to be justified.

Okay, I understand it, but I'm not sure why it's better. Please explain
why this is an improvement over the other approach.

Also note that "counter-examples" to rule (a) are anything for which
no one argument is easily-identifiable as the semantic focus.

EG, in a function like:
`adjudicate(plaintiff:defendant:circumstances:)` we can colorably
claim `circumstances` is a modifier-type parameter, but we don’t—and
shouldn’t!—treat either `plaintiff` or `defendant` as the
semantic-focus. If you have two focuses then you have no focus, as it
were.

For (b), the intuition is that whenever argument-order is irrelevant,
arguments should be unlabelled; thus e.g.:

- min/max: don’t label the arguments
- hypot: don’t label the arguments
- copysign: ideally, label the arguments
- atan2: ideally, label the arguments

Those last two may draw some quibbles from the math domain experts, but
I agree with the spirit.

…and so on. Note that these examples are all "free functions”; there
don’t seem to be many natural examples that *aren’t* free
functions.

colorMixer.blend(color1, color2)
track.fade(from: initialVolume, to: targetVolume)

Also, please don’t be mislead by your familiarity with
e.g. `copysign` and/or `atan2`; they are used here to illustrate a
general principle (argument-ordering) only, but in practice such
highly-familiar “legacy functions” might be best-off given
special-case handling.

Right, so we'd want different examples.

### III. Naming Functions/Ecosystem Rule

The previous sections essentially assumed the function names are
already-chosen (in line with existing conventions) and voice specific
argument-labeling preferences.

This section deals with a few changes to how function names should be chosen.

The over-arching consideration is what I’ve been calling the
“Ecosystem rule”: whenever a method a member of a “method family"—or
could foreseeably become a member of such—one should aim for
consistency in the base name, and use argument-labels as necessary;
note that method families need not *require* argument labels:

`contains(_: Point)`
`contains(_: Line)`
`contains(_: Shape)`

…but they *may* require them, as for example in the `login` function
that has already been discussed.

The “ecosystem-rule" can also be applied somewhat more-broadly;
consider the following name suggestions:

`animate(duration:animations:)`
`animate(duration:animations:completion:)`
`animate(duration:delay:options:animations:completion:)`
`animateUsingKeyFrames(duration:delay:options:animations:completion:)`
`animateUsingSpring(duration:delay:damping:initialVelocity:options:animations:completion:)`

…where the first three form an obvious family, and the next two are
obvious “cousins” of that family due to choice of base names.

A corollary of this policy is that the rule (3) suggestion—of omitting
something like `…ForIdentifier...` or `(forIdentifier:…)`—will
sometimes be overruled out of ecosystem concerns, but I suspect this
will be somewhat rare in practice.

For example, consider the following naming suggestions for the “tracks” example:

// solo method (not part of any family)
asset.trackWith(trackID)

// family
asset.allTracksWith(mediaCharacteristic: …)
asset.allTracksWith(mediaType: ...

// the below, instead of `trackWith` or `track(
asset.firstTrackWith(mediaCharacteristic: ...)
asset.firstTrackWith(mediaType: …)

…or the same again, but perhaps dropping the `With` if that’s the overall preference.

In any case, the overall goal behind the "ecosystem rule” is that
similar things should be named similarly, and when semantic
differences are small-enough it makes sense to use argument labels to
make distinctions; different base names should be for functions that
are at least a little different from each other.

This “rule” seems pretty darned vague, even after all this explanation.
I don't see how it could possibly be stated succinctly,

Furthermore, as I wrote to Erica, I have concerns about anything that
gives special treatment to method families, specifically:

* I'm wary of adding anything that encourages the creation of
“method families,” which have a higher cognitive overhead than many of
the alternatives.
* A guideline that forces you to change an existing non-overloaded API,
just because you are adding an overload, is problematic.

Understood, and noted, so I won’t press it, other than to point out
that method families have come up independently a few times, which may
deserve some consideration.

My own sense is they are rather more common in application-level code
than would be ideal in standard library code.

They do occur. I want them to look/feel good. I just don't want to
make special rules for them, if possible. Let's try to come up with
guidelines that work for everything with fewer exceptions.

--
-Dave

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

s.find(subString, .ignoringCase | .ignoringDiacriticalMarks) -> Range<String.Index>

func trimming(characterSet: NSCharacterSet) -> String func

s.trimming(.whitespaceAndNewlines)

Gah; you're right, here. Keeping a linguistic basis requires backing
off all the way from “sentence” to “phrase.” That's actually where I
started with this guideline, but thought I'd be able to strengthen it.
Saying “sentence or noun phrase” (if it works) might be better than just
“phrase” because “noun phrase” is always a term of art, whereas “phrase”
might just be interpreted casually.

I think sentence-or-noun-phrase covers things “linguistically", but it seems to carve out a pretty territory; other than special-cases (init, operators, label-less functions like `min`), I can’t think of anything that would be:

- reasonably-named (e.g. a name you’d actually want to use)
- *not* satisfying the proposed sentence-or-noun-phrase rule

…is there a good example of such?

Even if that’s right I’m not sure it’s a bad thing, but it’s still broad enough to give me a little pause; it may actually be the easiest way to carve out the non-init, non-operator, non-label-free-functions.

…than to win an argument over whether or not “add observer” describes
*enough* of the “primary semantics” of this method to fall under the
proposed guideline.

That seems less ambiguous to me than primary focus, and I believe a few
examples in the guidelines would be enough to settle most debates.

Fair enough. There’s still a subtle ambiguity in "primary semantics” (is it primary in the sense of “describing the most-important part" of the semantics, or is it “primary semantics” in the sense of “describes the majority/most(/etc.) of the semantics”), but examples should be enough to clear it up.

···

On Feb 4, 2016, at 1:47 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:

But that’s just an opinion, and on reflection I don’t think “primary
semantic focus” is the clearest formulation, either; I just don’t have
anything better.

This rule seems to cover e.g. `addObserver(_:forKeyPath:)` and
`addObserver(_:selector:name:object:)` and `encodeObject(_:forKey:)`
and `present(_:animated:completion:)` (née
`presentViewController(_:animated:completion:)`), and so on.

A point to bring up is that under these rules, the “evolution” of a
name would be different: the just-so story for how
`addObserver(_:forKeyPath:)` came to be so-called is that it *began*
as `add(observer:forKeyPath:)`, but b/c the `observer` argument is the
semantic focus it "made sense to move `observer` into the method
name”; that is, the assumption is that functions like
`addObserver(_:forKeyPath:)` are considered to be exceptions to the
"base convention” and need to be justified.

Okay, I understand it, but I'm not sure why it's better. Please explain
why this is an improvement over the other approach.

Also note that "counter-examples" to rule (a) are anything for which
no one argument is easily-identifiable as the semantic focus.

EG, in a function like:
`adjudicate(plaintiff:defendant:circumstances:)` we can colorably
claim `circumstances` is a modifier-type parameter, but we don’t—and
shouldn’t!—treat either `plaintiff` or `defendant` as the
semantic-focus. If you have two focuses then you have no focus, as it
were.

For (b), the intuition is that whenever argument-order is irrelevant,
arguments should be unlabelled; thus e.g.:

- min/max: don’t label the arguments
- hypot: don’t label the arguments
- copysign: ideally, label the arguments
- atan2: ideally, label the arguments

Those last two may draw some quibbles from the math domain experts, but
I agree with the spirit.

…and so on. Note that these examples are all "free functions”; there
don’t seem to be many natural examples that *aren’t* free
functions.

colorMixer.blend(color1, color2)
track.fade(from: initialVolume, to: targetVolume)

Also, please don’t be mislead by your familiarity with
e.g. `copysign` and/or `atan2`; they are used here to illustrate a
general principle (argument-ordering) only, but in practice such
highly-familiar “legacy functions” might be best-off given
special-case handling.

Right, so we'd want different examples.

### III. Naming Functions/Ecosystem Rule

The previous sections essentially assumed the function names are
already-chosen (in line with existing conventions) and voice specific
argument-labeling preferences.

This section deals with a few changes to how function names should be chosen.

The over-arching consideration is what I’ve been calling the
“Ecosystem rule”: whenever a method a member of a “method family"—or
could foreseeably become a member of such—one should aim for
consistency in the base name, and use argument-labels as necessary;
note that method families need not *require* argument labels:

`contains(_: Point)`
`contains(_: Line)`
`contains(_: Shape)`

…but they *may* require them, as for example in the `login` function
that has already been discussed.

The “ecosystem-rule" can also be applied somewhat more-broadly;
consider the following name suggestions:

`animate(duration:animations:)`
`animate(duration:animations:completion:)`
`animate(duration:delay:options:animations:completion:)`
`animateUsingKeyFrames(duration:delay:options:animations:completion:)`
`animateUsingSpring(duration:delay:damping:initialVelocity:options:animations:completion:)`

…where the first three form an obvious family, and the next two are
obvious “cousins” of that family due to choice of base names.

A corollary of this policy is that the rule (3) suggestion—of omitting
something like `…ForIdentifier...` or `(forIdentifier:…)`—will
sometimes be overruled out of ecosystem concerns, but I suspect this
will be somewhat rare in practice.

For example, consider the following naming suggestions for the “tracks” example:

// solo method (not part of any family)
asset.trackWith(trackID)

// family
asset.allTracksWith(mediaCharacteristic: …)
asset.allTracksWith(mediaType: ...

// the below, instead of `trackWith` or `track(
asset.firstTrackWith(mediaCharacteristic: ...)
asset.firstTrackWith(mediaType: …)

…or the same again, but perhaps dropping the `With` if that’s the overall preference.

In any case, the overall goal behind the "ecosystem rule” is that
similar things should be named similarly, and when semantic
differences are small-enough it makes sense to use argument labels to
make distinctions; different base names should be for functions that
are at least a little different from each other.

This “rule” seems pretty darned vague, even after all this explanation.
I don't see how it could possibly be stated succinctly,

Furthermore, as I wrote to Erica, I have concerns about anything that
gives special treatment to method families, specifically:

* I'm wary of adding anything that encourages the creation of
“method families,” which have a higher cognitive overhead than many of
the alternatives.
* A guideline that forces you to change an existing non-overloaded API,
just because you are adding an overload, is problematic.

Understood, and noted, so I won’t press it, other than to point out
that method families have come up independently a few times, which may
deserve some consideration.

My own sense is they are rather more common in application-level code
than would be ideal in standard library code.

They do occur. I want them to look/feel good. I just don't want to
make special rules for them, if possible. Let's try to come up with
guidelines that work for everything with fewer exceptions.

--
-Dave

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I believe David is saying that's an orthogonal issue. i.e.

a.readFromSource(u, ofType: b)
vs.
a.readFrom(source: u, ofType: b)
vs.
a.read(fromSource: u, ofType: b)

Is a separable issue from:

a.readFrom(source: u, ofType: b)
vs.
a.readFrom(u, ofType: b)

-or-

a.read(source: u, ofType: b)
vs.
a.read(u, ofType: b)

I don't agree they are completely orthogonal since I think we potentially end up with different results based on which order these two issues are addressed, but I still think David's point is valuable in thinking about What the guidelines should be.

···

On Feb 5, 2016, at 03:37, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

Here are then the examples of good use cases:

No First Argument Labels

a.readFrom(u, ofType: b) // yes, satisfies rules 1 and 2
a.readFrom(source: u, ofType: b) // no, source is explicitly derived by “From”

Keep First Argument Labels Labels

a.read(u, ofType: b) // no, it’s not clear what u’s role is
a.read(source: u, ofType: b) // yes, the source being read is not clear, it could be stdin,

Okay, but which should it be? `readFrom(_:...)` or `read(source:...)`? These two APIs have very different flavors, and you seem to be saying that either one is okay. That's not a very good thing if we're trying to define what "Swifty" APIs look like.

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I'm saying that which one doesn't matter for how argument labels should be used.

I think the question of which is more "swifty" is a good one to ask. However, I also do not think it always has the same answer.

···

Sent from my iPhone

On Feb 5, 2016, at 12:37 AM, Brent Royal-Gordon <brent@architechies.com> wrote:

Here are then the examples of good use cases:

No First Argument Labels

a.readFrom(u, ofType: b) // yes, satisfies rules 1 and 2
a.readFrom(source: u, ofType: b) // no, source is explicitly derived by “From”

Keep First Argument Labels Labels

a.read(u, ofType: b) // no, it’s not clear what u’s role is
a.read(source: u, ofType: b) // yes, the source being read is not clear, it could be stdin,

Okay, but which should it be? `readFrom(_:...)` or `read(source:...)`? These two APIs have very different flavors, and you seem to be saying that either one is okay. That's not a very good thing if we're trying to define what "Swifty" APIs look like.

--
Brent Royal-Gordon
Architechies

One good way to make this determination, if the answer isn't obvious:
“which choice makes for simpler guidelines?”

···

on Fri Feb 05 2016, Brent Royal-Gordon <swift-evolution@swift.org> wrote:

Here are then the examples of good use cases:

No First Argument Labels

a.readFrom(u, ofType: b) // yes, satisfies rules 1 and 2
a.readFrom(source: u, ofType: b) // no, source is explicitly derived by “From”

Keep First Argument Labels Labels

a.read(u, ofType: b) // no, it’s not clear what u’s role is
a.read(source: u, ofType: b) // yes, the source being read is not clear, it could be stdin,

Okay, but which should it be? `readFrom(_:...)` or `read(source:...)`?
These two APIs have very different flavors, and you seem to be saying
that either one is okay. That's not a very good thing if we're trying
to define what "Swifty" APIs look like.

--
-Dave

I believe everything you've written here is actually expressed, in
practice, by the guidelines I've suggested. I went through many of the
same thought processes in working the guidelines out. The trick is to
capture these thoughts in something simply stated that has the right
effect. If you think the guideline I suggested doesn't have the right
effect, please demonstrate a case where your thought process would lead
to a different API choice.

I think our positions are basically compatible; I simply find an explanation like this useful to understand *why* the rules are as they are. Many of the objections and alternative proposals I've been seeing don't seem to be informed by this philosophy.

I'm wondering if we should write up a rationale for the API guidelines as a design document that people can optionally read to help them understand the guiding philosophy behind the API guidelines.

Yeah, or there can be a section in the guidelines, or we can find another way to integrate rationale. That should definitely be available somehow if people find it helpful.

That would be great! I always find it helpful to read a rationale for rules or design decisions to better understand them.

(I also wonder if introducing the concept of an option in a more explicit way might clarify some rules; for instance, the rule about giving the first parameter a label if it's defaulted is really about giving it a label if it's an *option*.)

I think it would possibly clarify the *rationale* behind some rules, but that's not the same as stating the rule clearly. The fewer specific guidelines and angles you're required to use to analyze any given API design scenario, the better.

I totally agree (though the rationale would still be valuable).

-Thorsten

···

Am 04.02.2016 um 17:55 schrieb Dave Abrahams via swift-evolution <swift-evolution@swift.org>:

On Feb 4, 2016, at 12:10 AM, Brent Royal-Gordon <brent@architechies.com> wrote:

If we can clearly define what "option" means and replace some other criterion with that one, then it could be an improvement, but I don't see a clear path from here to there.

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

So the preposition should move into the argument label if the argument is optional?

copy(withZone: zone = nil)

Zone is redundant with type information.

copy(with:)?
copy(withZone:)?

···

Sent from my iPhone

On Feb 6, 2016, at 9:21 AM, Thorsten Seitz <tseitz42@icloud.com> wrote:

-Thorsten

Am 06.02.2016 um 14:45 schrieb Matthew Judge via swift-evolution <swift-evolution@swift.org>:

Very first method

copyWith(zone: Zone = nil)

can be called as

copyWith()

I'm assuming this is still something we don't want right?

On Feb 6, 2016, at 02:16, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

On Feb 5, 2016, at 1:32 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:

Given all the awesome feedback I've gotten on this thread, I went back
to the drawing board and came up with something new; I think this one
works. The previously-stated goals still apply:

[snip goals]

P.S. Doug is presently working on generating new importer results, based
    on these guidelines, for your perusal. They should be ready soon.

Here’s a link:

  Split first selector piece into base name/first argument label. by DougGregor · Pull Request #10 · apple/swift-3-api-guidelines-review · GitHub

Feedback welcome!

  - Doug

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

If the argument has a default.

···

On Feb 6, 2016, at 12:21, Thorsten Seitz <tseitz42@icloud.com> wrote:

So the preposition should move into the argument label if the argument is optional?

copy(withZone: zone = nil)

-Thorsten

Am 06.02.2016 um 14:45 schrieb Matthew Judge via swift-evolution <swift-evolution@swift.org>:

Very first method

copyWith(zone: Zone = nil)

can be called as

copyWith()

I'm assuming this is still something we don't want right?

On Feb 6, 2016, at 02:16, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

On Feb 5, 2016, at 1:32 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:

Given all the awesome feedback I've gotten on this thread, I went back
to the drawing board and came up with something new; I think this one
works. The previously-stated goals still apply:

[snip goals]

P.S. Doug is presently working on generating new importer results, based
    on these guidelines, for your perusal. They should be ready soon.

Here’s a link:

  Split first selector piece into base name/first argument label. by DougGregor · Pull Request #10 · apple/swift-3-api-guidelines-review · GitHub

Feedback welcome!

  - Doug

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Err, yes, that's what I meant. 'Optional' at the call site due to the default, but of course 'optional' is a bad choice of wording due to the optional type. Thanks.

-Thorsten

···

Am 06.02.2016 um 18:44 schrieb Matthew Judge <matthew.judge@gmail.com>:

If the argument has a default.

On Feb 6, 2016, at 12:21, Thorsten Seitz <tseitz42@icloud.com> wrote:

So the preposition should move into the argument label if the argument is optional?

copy(withZone: zone = nil)

-Thorsten

Am 06.02.2016 um 14:45 schrieb Matthew Judge via swift-evolution <swift-evolution@swift.org>:

Very first method

copyWith(zone: Zone = nil)

can be called as

copyWith()

I'm assuming this is still something we don't want right?

On Feb 6, 2016, at 02:16, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

On Feb 5, 2016, at 1:32 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:

Given all the awesome feedback I've gotten on this thread, I went back
to the drawing board and came up with something new; I think this one
works. The previously-stated goals still apply:

[snip goals]

P.S. Doug is presently working on generating new importer results, based
    on these guidelines, for your perusal. They should be ready soon.

Here’s a link:

  Split first selector piece into base name/first argument label. by DougGregor · Pull Request #10 · apple/swift-3-api-guidelines-review · GitHub

Feedback welcome!

  - Doug

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I guess I would consider it grammatical if it was a.logInAs(b), and
arguably if we say names and IDs don't need a preceding noun, then
a.logIn(b) could be sufficient in this case, but I guess it's the
lowercasing of "login" that makes me read "a.login(b)" as ungrammatical.

There are *gonna* be gray areas no matter what we do; this might be one
of them.

···

on Sat Feb 06 2016, Thorsten Seitz <swift-evolution@swift.org> wrote:

Am 06.02.2016 um 02:59 schrieb Dave Abrahams via swift-evolution <swift-evolution@swift.org>:

When the natural semantic relationship between the arguments is
stronger than the relationship between the method name and the first
argument, use first argument labels, whether the label is a noun or a
preposition:

a.move(from: b, to: c)
a.login(username: b, password: c)

You don't need this additional guideline to get

a.login(username: b, password: c)

a login b is not part of a grammatical phrase with the right semantics,
therefore the first argument gets a label.

Could you explain why `a login b` is not sufficient? Is it because I
cannot log in a username but only a user?

--
-Dave

Sent from my iPhone

So the preposition should move into the argument label if the argument is optional?

copy(withZone: zone = nil)

Zone is redundant with type information.

copy(with:)?
copy(withZone:)?

Wouldn’t it be possible to simply drop the copyWithZone: method that is deprecated for some times now (To quote the doc: Zones are ignored on iOS and 64-bit runtime on OS X. You should not use zones in current development).

···

Le 6 févr. 2016 à 21:15, Douglas Gregor via swift-evolution <swift-evolution@swift.org> a écrit :
On Feb 6, 2016, at 9:21 AM, Thorsten Seitz <tseitz42@icloud.com> wrote:

-Thorsten

Am 06.02.2016 um 14:45 schrieb Matthew Judge via swift-evolution <swift-evolution@swift.org>:

Very first method

copyWith(zone: Zone = nil)

can be called as

copyWith()

I'm assuming this is still something we don't want right?

On Feb 6, 2016, at 02:16, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

On Feb 5, 2016, at 1:32 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:

Given all the awesome feedback I've gotten on this thread, I went back
to the drawing board and came up with something new; I think this one
works. The previously-stated goals still apply:

[snip goals]

P.S. Doug is presently working on generating new importer results, based
    on these guidelines, for your perusal. They should be ready soon.

Here’s a link:

  Split first selector piece into base name/first argument label. by DougGregor · Pull Request #10 · apple/swift-3-api-guidelines-review · GitHub

Feedback welcome!

  - Doug

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

So the preposition should move into the argument label if the argument is optional?

copy(withZone: zone = nil)

That's a good idea.

···

on Sat Feb 06 2016, Thorsten Seitz <swift-evolution@swift.org> wrote:

-Thorsten

Am 06.02.2016 um 14:45 schrieb Matthew Judge via swift-evolution >> <swift-evolution@swift.org>:

Very first method

copyWith(zone: Zone = nil)

can be called as

copyWith()

I'm assuming this is still something we don't want right?

On Feb 6, 2016, at 02:16, Douglas Gregor via swift-evolution >>> <swift-evolution@swift.org> wrote:

On Feb 5, 2016, at 1:32 PM, Dave Abrahams via swift-evolution >>>> <swift-evolution@swift.org> wrote:

Given all the awesome feedback I've gotten on this thread, I went back
to the drawing board and came up with something new; I think this one
works. The previously-stated goals still apply:

[snip goals]

P.S. Doug is presently working on generating new importer results, based
    on these guidelines, for your perusal. They should be ready soon.

Here’s a link:

  Split first selector piece into base name/first argument label. by DougGregor · Pull Request #10 · apple/swift-3-api-guidelines-review · GitHub

Feedback welcome!

  - Doug

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

--
-Dave