When to use argument labels (a new approach)

Well, let me just say that there may be room for these kinds of principles to evolve in the future, but those are the constraints we have to work within to get this first round of guidelines out. This importer technique can only carry us so far: Swift will acquire new expressive capabilities, and we (Apple) will have to consider other ways to make sure Cocoa's APIs keep up.

-Dave

···

On Feb 4, 2016, at 8:21 AM, Matt Whiteside <mwhiteside.dev@gmail.com> wrote:

On Feb 4, 2016, at 00:23, Radosław Pietruszewski via swift-evolution <swift-evolution@swift.org> wrote:

Personally I think it’s a shame to constrain ourselves to (imho) inferior naming convention because it’s what existing ObjC APIs use. Definitely understandable, but considering this isn’t a _really_ super common pattern, I think it would be just fine to leave the translations in good-enough state (whatever the decision on SE-0005 is), and follow a new convention in new Swift APIs.

+1. Strongly agree on this.

Awesome for putting this together. I think the rules add some needed
clarity, but they have the bias that the first argument *should* have
a defaulted empty parameter label.

I disagree with this as a premise. IMO they don't have any such bias.
The guideline says, only under these very specific conditions does one
omit an argument label. That would indicate, if anything, a bias
towards *having* a label.

In one respect, I agree that the guidelines are written with the intention to be more open on the existence of the first argument label. However, when you apply the guidelines, the “very specific conditions” seem to qualify the vast majority of cases.

Plus, there is this:

For better or worse, it is a requirement that Cocoa as imported very
closely approximates full conformance to the guidelines we choose. We
are shooting for consistency across APIs used in swift.

And all of the following conversations around it. This is why I say there seems to be a bias towards APIs with not having an argument label.

All that being sad said, my post was really about two things:

  1. clarifying the usage of labels in more than just the first parameter's slot while also simplifying the rule landscape, and
  2. debating rule #2, which really seems about naming APIs, not really about argument label usage

I think my critique is still valid, but I'll focus it directly on when the label should be present and leave the debate for rule #2 elsewhere. I think the two are orthogonal issues, and sometimes the “better choice" is really subjective to the APIs being built. However, whether the argument label should exist is actually a much more straight forward issue to address.

The stated goals of the proposal were these:

* 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.

I think the proposal falls short of the first point: the proposal really only addresses the first argument. There are cases when the other arguments should not be present as well.

If we start with the premise that all parameters should be labeled, then we can have a set of exclusionary rules to help guide the process. I start here because I believe it’s easier to state the cases on when to remove the labels instead of trying to provide rules for both sides.

The rules:

The purpose of an argument label is to provide clear context of the argument’s role within the function.

No argument labels are necessary when:

  1. The parameters have a unambiguous meaning with the intent of the function
  2. The label would simply be repeating information that is already explicitly or implicitly derived from the function’s name regarding it’s intent

Here are then the examples of good use cases:

No First Argument Labels

a.contains(b) // yes, satisfies rules 1 and 2
a.contains(object: b) // no, object is adds no value: it’s implicitly there

a.merging(b) // yes, satisfies rules 1 and 2
a.merging(items: b) // no, items adds no value: it’s implicitly there

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

a.addObserver(o) // yes, o has a clear role
a.addObserver(observer: o) // no, duplicating info

a.tracksHavingMediaType("Wax Cylinder") // yes, the first param’s role is clear
a.tracksHavingMediaType(mediaType: "Wax Cylinder") // no, duplicating info

Keep First Argument Labels Labels

a.dismiss(b) // no, this seems to be dismissing b
a.dismiss(animated: b) // yes, intent is to dismiss; b is a modifier on how to animate.

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,
                              // or it could be reading from a, etc…

a.tracksHaving("Wax Cylinder") // no, the param’s role is not clear
a.tracksHaving(mediaType: "Wax Cylinder") // yes, provides clear context for the param

Ambiguous Argument Labels - Really up to the API surface

a.moveTo(x: 300, y: 400) // it may not be clear that (x,y) is the movement plane
a.moveTo(300, 400) // a may provide explicit context for a 2D plane or coordinates

The non-labeled `moveTo` example is also an illustration of why `max` would have no labels:

max(x, y) // yes, x's and y’s role is unambiguous
max(a: x, b: y) // no, there is a clear role for each argument

I think this ruleset is clearer and provides guidance for the various choices that are before us with your rule #2. I also think that arguments on how to name an API like “tracksHavingMediaType” are better left elsewhere. These guidelines still help you decide on the argument placement though regardless of which API works best for your purposes.

-David

···

On Feb 3, 2016, at 2:42 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:
on Tue Feb 02 2016, David Owens II <swift-evolution@swift.org> wrote:

Hello Dave,

This is really shaping up nicely. Unfortunately, I still have difficulties with:

a.moveFrom(b, to: c)

I understand the need to have simply guidelines, but this just reads wrong to me, especially because b and c have equal importance. This is so much better IMHO:

a.move(from: b, to: c)

But I’m not sure how to write a simple rule to explain this reasoning.

I agree and I prefer `a.move(from: b, to: c)`. However, I think the rule is pretty clear already:

Rule: If the first argument is part of a prepositional phrase, put the parenthesis immediately after the preposition.

"move from" is the prepositional phrase for the function, so move the "from" label into the base name.

-David

···

On Feb 5, 2016, at 1:42 PM, David Hart via swift-evolution <swift-evolution@swift.org> wrote:

Hello Dave,

This is really shaping up nicely. Unfortunately, I still have difficulties with:

a.moveFrom(b, to: c)

I understand the need to have simply guidelines, but this just reads
wrong to me, especially because b and c have equal importance. This is
so much better IMHO:

a.move(from: b, to: c)

Again, I understand the aesthetic preference for this form; I even
prefer it myself. On the other hand, it seems to me that the difference
between the two forms reflects a point of emphasis that isn't crucial to
understandability at the use-site. That's why I'm asking whether you
*can't live with* the results. Are you merely expressing a strong
preference, or are you saying the first form is too awful to abide?

But I’m not sure how to write a simple rule to explain this reasoning.

Any *additional* rule is more complexity in the guidelines, which would
have to pay for itself in some objective terms. Since (I'm assuming
you'll agree that) it doesn't make a difference to understandability,
what objective criteria would you use to justify accomodating cases like
this?

···

on Fri Feb 05 2016, David Hart <swift-evolution@swift.org> wrote:

David.

On 05 Feb 2016, at 22:32, 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:

* describe when and where to use argument labels
* require labels in many of the cases people have asked for them
* are understandable by humans (this means relatively simple)
* preserve important semantics communicated by existing APIs.

Please keep in mind that it is a non-goal to capture considerations we
think have a bearing on good names (such as relatedness of parameters):
it's to create simple guidelines that have the right effect in nearly
all cases.

A. When arguments can't be usefully distinguished from one another, none
  should have argument labels, e.g. min(x,y), zip(x,y,z).

B. Otherwise,

1. At the call site, a first parameter that has no argument label must
    form part of a grammatical phrase that starts with the basename, less
    any trailing nouns.

      print(x)
      a.contains(b)
      a.mergeWith(b)
      a.addGestureRecognizer(x)
           ^~~~~~~~~~~~~~~~~ trailing noun

    This phrase must have the correct semantic implications, so, e.g.

      a.dismiss(b) // no, unless a is really dismissing b
      a.dismissAnimated(b) // no, not grammatical
      a.dismiss(animated: b) // yes, using a label

2. If the first argument is part of a prepositional phrase, put the
    parenthesis immediately after the preposition.

      a.encodeWith(b)
      a.moveFrom(b, to: c)

    Thus, if words are required for any reason between the preposition
    and the first argument, they go into the first argument label.

      a.tracksWith(mediaType: b, composer: c)
      a.moveTo(x: 22, y: 99)

Notes:

a. I would recommend prepositions other than "with" in nearly all
  cases, but that's not the point of these rules.
b. I can understand the aesthetic appeal of

   a.move(from: b, to: c)

  but I believe it is not a clear enough improvement to justify
  additional complexity in the guidelines.

Questions:

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?

2. Are there any cases where you'd be confused about how to apply these
  guidelines?

Thanks in advance for all your valuable input!

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

--
-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

Thanks!

···

on Fri Feb 05 2016, David Owens II <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:

* describe when and where to use argument labels
* require labels in many of the cases people have asked for them
* are understandable by humans (this means relatively simple)
* preserve important semantics communicated by existing APIs.

Please keep in mind that it is a non-goal to capture considerations we
think have a bearing on good names (such as relatedness of parameters):
it's to create simple guidelines that have the right effect in nearly
all cases.

A. When arguments can't be usefully distinguished from one another, none
  should have argument labels, e.g. min(x,y), zip(x,y,z).

+1. Thanks for adding this set.

B. Otherwise,

1. At the call site, a first parameter that has no argument label must
    form part of a grammatical phrase that starts with the basename, less
    any trailing nouns.

      print(x)
      a.contains(b)
      a.mergeWith(b)
      a.addGestureRecognizer(x)
           ^~~~~~~~~~~~~~~~~ trailing noun

    This phrase must have the correct semantic implications, so, e.g.

      a.dismiss(b) // no, unless a is really dismissing b
      a.dismissAnimated(b) // no, not grammatical
      a.dismiss(animated: b) // yes, using a label

+1. Seems good.

2. If the first argument is part of a prepositional phrase, put the
    parenthesis immediately after the preposition.

      a.encodeWith(b)
      a.moveFrom(b, to: c)

    Thus, if words are required for any reason between the preposition
    and the first argument, they go into the first argument label.

      a.tracksWith(mediaType: b, composer: c)
      a.moveTo(x: 22, y: 99)

+1. Looks good and I like the distinction, especially between `moveFrom` and `moveTo`.

Notes:

a. I would recommend prepositions other than "with" in nearly all
  cases, but that's not the point of these rules.
b. I can understand the aesthetic appeal of

   a.move(from: b, to: c)

  but I believe it is not a clear enough improvement to justify
  additional complexity in the guidelines.

Questions:

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?

2. Are there any cases where you'd be confused about how to apply these
  guidelines?

Thanks in advance for all your valuable input!

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

--
-Dave

+1 to all of it. Great update.

--
-Dave

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:

* describe when and where to use argument labels
* require labels in many of the cases people have asked for them
* are understandable by humans (this means relatively simple)
* preserve important semantics communicated by existing APIs.

Please keep in mind that it is a non-goal to capture considerations we
think have a bearing on good names (such as relatedness of parameters):
it's to create simple guidelines that have the right effect in nearly
all cases.

A. When arguments can't be usefully distinguished from one another, none
  should have argument labels, e.g. min(x,y), zip(x,y,z).

Personal bugaboo. Guidance should guide: Avoid argument labels when
arguments cannot be usefully distinguished from one another.

Noted, but this is not intended to be wording for the guidelines
document; it's intended to describe the substance of what the guidelines
will say..

B. Otherwise,

1. At the call site, a first parameter that has no argument label must
    form part of a grammatical phrase that starts with the basename, less
    any trailing nouns.

      print(x)
      a.contains(b)
      a.mergeWith(b)
      a.addGestureRecognizer(x)
           ^~~~~~~~~~~~~~~~~ trailing noun

    This phrase must have the correct semantic implications, so, e.g.

      a.dismiss(b) // no, unless a is really dismissing b
      a.dismissAnimated(b) // no, not grammatical
      a.dismiss(animated: b) // yes, using a label

Skip parameter labels when the first argument completes a
grammatically meaningful phrase starting with the base name (and apart
from trailing nouns).

No; “completing” a phrase is needless complexity. It isn't needed to
make these guidelines work.

But again, I don't know *why* you're rewriting what I wrote. If you're
going to respond with a rewrite, please explain how you think your
rewrite improves on the original.

      a.dismiss(b) // Not unless a is really dismissing some instance of b

2. If the first argument is part of a prepositional phrase, put the
    parenthesis immediately after the preposition.

      a.encodeWith(b)
      a.moveFrom(b, to: c)

    Thus, if words are required for any reason between the preposition
    and the first argument, they go into the first argument label.

      a.tracksWith(mediaType: b, composer: c)
      a.moveTo(x: 22, y: 99)

When using prepositional phrases, use parentheses after the
preposition. Place any supporting words into first argument labels.

a.dismissUsing(animation: b)
a.tracksOf(mediaType: b, composer: c)
a.moveTo(x: 22, y: 99)

AGAINST: this worked better with your "creating new instance rule" and
should probably be called out as such. I'd like to see the "if it
works like an initializer it should be named with initializer label"
bits come back.

a.colorWith(red: r, green: g, blue: b, alpha: a)

I think this should probably be:

a.color(red:, green:, blue:, alpha:)

That's already covered under "omit needless words." "With" is vacuous
here, so you drop it.

Notes:

a. I would recommend prepositions other than "with" in nearly all
  cases, but that's not the point of these rules.

When using "with" as your go-to preposition, carefully consider
whether other more meaningful prepositions could apply. "With" tends
to describe use at a call site rather than method or function
semantics.

Yes, a guideline for this could be useful, but no, I'm not asking for
feedback on that. This is in a note because I'm merely accounting for
the fact that I had used "Having" and other such prepositions in earlier
drafts, and now I'm going back to "With" so that people will more
readily see the connection with foundation APIs.

b. I can understand the aesthetic appeal of

   a.move(from: b, to: c)

  but I believe it is not a clear enough improvement to justify
  additional complexity in the guidelines.

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. As for

  a.move(from: b, to: c)

the extra complication in the guidelines required to make this happen
makes for aesthetically pleasing results in a few cases, that don't seem
substantially more comprehensible. If you want to argue for doing it,
it should be justified on the basis of something more objective than "it
feels better to me."

-- E, always attempting to be helpful, but understanding your
frustration at getting this kind of feedback

I appreciate the understanding. What would actually help would be to
accompany your rewrites by rationales and explanations. Without them, I
have no idea how to evaluate your suggestions.

···

on Fri Feb 05 2016, Erica Sadun <swift-evolution@swift.org> wrote:

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

--
-Dave

Again, I understand the aesthetic preference for this form; I even
prefer it myself. On the other hand, it seems to me that the difference
between the two forms reflects a point of emphasis that isn't crucial to
understandability at the use-site. That's why I'm asking whether you
*can't live with* the results. Are you merely expressing a strong
preference, or are you saying the first form is too awful to abide?

But I’m not sure how to write a simple rule to explain this reasoning.

Any *additional* rule is more complexity in the guidelines, which would
have to pay for itself in some objective terms. Since (I'm assuming
you'll agree that) it doesn't make a difference to understandability,
what objective criteria would you use to justify accomodating cases like
this?

I’m not an extreme enough person to say that it’s too awful to abide :), but I definitely lie more on that side of the fence than the simple strong preference.

To come back to your other examples, even if I prefer the forms you described in your new thread:

a.dismiss(animated: b)
a.tracksWith(mediaType: b, composer: c)

The alternatives:

a.dismissAnimated(b)
a.tracksWithMediaType(b, composer: c)

Shock me much much less than:

a.moveFrom(b, to: c)

That last one is many times less readable to me.

David.

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

Quick remark: it looks like completion-handlers will benefit from a special-case rule to trim a trailing preposition, e.g. consider this:

extension AVAssetExportSession {
- func determineCompatibleFileTypesWithCompletionHandler(handler: ([String]) -> Void)
+ func determineCompatibleFileTypesWith(completionHandler handler: ([String]) -> Void)
}

…which should probably be this:

func determineCompatibleFileTypes(completionHandler handler: ([String]) -> Void)

···

On Feb 6, 2016, at 1:16 AM, 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 <mailto: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

Great points Erica.

I just wanted to chip in and say thanks to everyone participating.
Discussing it thoroughly is important, whether the individual discussions
result in changes to the proposal or not. Finding a clear, well reasoned
and thoroughly discussed guidelines will have a massive benefit to Swift.

The benefit of clear API guidelines on code quality is likely to have a
further reaching and longer lasting effect than even many of the more
concrete proposals to clean up syntax and improve the type system.

···

On Sat, Feb 6, 2016 at 11:29 AM, Erica Sadun via swift-evolution < swift-evolution@swift.org> wrote:

> On Feb 5, 2016, at 2: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:
>
> * describe when and where to use argument labels
> * require labels in many of the cases people have asked for them
> * are understandable by humans (this means relatively simple)
> * preserve important semantics communicated by existing APIs.
>
> Please keep in mind that it is a non-goal to capture considerations we
> think have a bearing on good names (such as relatedness of parameters):
> it's to create simple guidelines that have the right effect in nearly
> all cases.
>
> A. When arguments can't be usefully distinguished from one another, none
> should have argument labels, e.g. min(x,y), zip(x,y,z).

Personal bugaboo. Guidance should guide: Avoid argument labels when
arguments cannot be usefully distinguished from one another.

>
> B. Otherwise,
>
> 1. At the call site, a first parameter that has no argument label must
> form part of a grammatical phrase that starts with the basename, less
> any trailing nouns.
>
> print(x)
> a.contains(b)
> a.mergeWith(b)
> a.addGestureRecognizer(x)
> ^~~~~~~~~~~~~~~~~ trailing noun
>
> This phrase must have the correct semantic implications, so, e.g.
>
> a.dismiss(b) // no, unless a is really dismissing b
> a.dismissAnimated(b) // no, not grammatical
> a.dismiss(animated: b) // yes, using a label

Skip parameter labels when the first argument completes a grammatically
meaningful phrase starting with the base name (and apart from trailing
nouns).

> a.dismiss(b) // Not unless a is really dismissing some
instance of b

>
> 2. If the first argument is part of a prepositional phrase, put the
> parenthesis immediately after the preposition.
>
> a.encodeWith(b)
> a.moveFrom(b, to: c)
>
> Thus, if words are required for any reason between the preposition
> and the first argument, they go into the first argument label.
>
> a.tracksWith(mediaType: b, composer: c)
> a.moveTo(x: 22, y: 99)

When using prepositional phrases, use parentheses after the preposition.
Place any supporting words into first argument labels.

a.dismissUsing(animation: b)
a.tracksOf(mediaType: b, composer: c)
a.moveTo(x: 22, y: 99)

AGAINST: this worked better with your "creating new instance rule" and
should probably be called out as such. I'd like to see the "if it works
like an initializer it should be named with initializer label" bits come
back.

a.colorWith(red: r, green: g, blue: b, alpha: a)

I think this should probably be:

a.color(red:, green:, blue:, alpha:)

>
> Notes:
>
> a. I would recommend prepositions other than "with" in nearly all
> cases, but that's not the point of these rules.

When using "with" as your go-to preposition, carefully consider whether
other more meaningful prepositions could apply. "With" tends to describe
use at a call site rather than method or function semantics.

> b. I can understand the aesthetic appeal of
>
> a.move(from: b, to: c)
>
> but I believe it is not a clear enough improvement to justify
> additional complexity in the guidelines.

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)

-- E, always attempting to be helpful, but understanding your frustration
at getting this kind of feedback

>
> Questions:
>
> 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?
>
> 2. Are there any cases where you'd be confused about how to apply these
> guidelines?
>
> Thanks in advance for all your valuable input!
>
> P.S. Doug is presently working on generating new importer results, based
> on these guidelines, for your perusal. They should be ready soon.
>
> --
> -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

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

Well, I might be the only one, but I don’t like these diffs. There are only a few cases where I don’t prefer the original function signature. Function names ending in prepositions with no objects look odd to me. It seems like a strange choice to make the split in the middle of the prepositional phrase.

If, as now proposed, the description of the main focus of the function call is going to be moved from the function name to a label for the first parameter, why not just drop the preposition entirely? As in:

func copy(zone zone: Zone = nil) -> AnyObject
func loadValuesAsynchronously(keys keys: [String], completionHandler handler: (() -> Void)? = nil)
func tracks(mediaType mediaType: String) -> [AVAssetTrack]

Or put it in the argument label, to keep the prepositional phrase intact:

func copy(withZone zone: Zone = nil) -> AnyObject
func loadValuesAsynchronously(forKeys keys: [String], completionHandler handler: (() -> Void)? = nil)
func tracks(withMediaType mediaType: String) -> [AVAssetTrack]

—CK

···

On Feb 5, 2016, at 11:16 PM, 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 <mailto:swift-evolution@swift.org>> wrote:

Feedback welcome!

  - Doug

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

That feature is something I like very much in Swift and being able to name the method

a.moveFile(source: b, to destination: c)

is a great thing as it makes the call site read much better.

Not using a great feature of a language just because somebody might not be used to it because other languages domnot have this feature would make me sad.

-Thorsten

···

Am 06.02.2016 um 19:27 schrieb Tino Heth via swift-evolution <swift-evolution@swift.org>:

It's a very long (and complicated) thread, so I guess the following thought isn't new:

a.move(from: b, to: c)

In cases like this, where there are nouns that describe the parameters as good as "from" and "to", so I would call the method

a.moveFile(source: b, destination: c)

("File" is just a placeholder; I think move is to ambiguous)

The advantage of nouns is that they are well suited as variable names as well; the ability to have separate internal and external names is rather exotic, so I think it looks less strange to somebody without an Objective-C/Swift background.

I like these revisions quite a bit.

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:

* describe when and where to use argument labels
* require labels in many of the cases people have asked for them
* are understandable by humans (this means relatively simple)
* preserve important semantics communicated by existing APIs.

Please keep in mind that it is a non-goal to capture considerations we
think have a bearing on good names (such as relatedness of parameters):
it's to create simple guidelines that have the right effect in nearly
all cases.

A. When arguments can't be usefully distinguished from one another, none
  should have argument labels, e.g. min(x,y), zip(x,y,z).

B. Otherwise,

1. At the call site, a first parameter that has no argument label must
    form part of a grammatical phrase that starts with the basename, less
    any trailing nouns.

      print(x)
      a.contains(b)
      a.mergeWith(b)
      a.addGestureRecognizer(x)
           ^~~~~~~~~~~~~~~~~ trailing noun

    This phrase must have the correct semantic implications, so, e.g.

      a.dismiss(b) // no, unless a is really dismissing b
      a.dismissAnimated(b) // no, not grammatical
      a.dismiss(animated: b) // yes, using a label

2. If the first argument is part of a prepositional phrase, put the
    parenthesis immediately after the preposition.

      a.encodeWith(b)
      a.moveFrom(b, to: c)

    Thus, if words are required for any reason between the preposition
    and the first argument, they go into the first argument label.

      a.tracksWith(mediaType: b, composer: c)
      a.moveTo(x: 22, y: 99)

Notes:

a. I would recommend prepositions other than "with" in nearly all
  cases, but that's not the point of these rules.
b. I can understand the aesthetic appeal of

   a.move(from: b, to: c)

I agree with the aesthetic and also not wanting to complicate the rules to cover it.

I also think this is a bad example, though, b/c in practice one would
*hope* that such calls would look more like:

container.move(element, from: currentIndex, to: destinationIndex)

…and whatnot;

I don't understand how this one works. In most cases the call would
have lots of redundancy:

  container.move(container[currentIndex], from: currentIndex, to: destinationIndex)
  ^^^^^^^^^ ^^^^^^^^^ ~~~~~~~~~~~~ ~~~~~~~~~~~~

Why make the caller come up with the element value here?

I suspect that the `move(from:to:)` case may be getting strong
reactions in part b/c it’s actually an unusually-poorly-named method
to begin with.

Also, as these are just guidelines, in third party code we can deviate
from them (at our own peril, but we can...).

  but I believe it is not a clear enough improvement to justify
  additional complexity in the guidelines.

Questions:

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?

2. Are there any cases where you'd be confused about how to apply these
  guidelines?

I do find some of the rules worded in ways that are hard to apply in
the presence of trailing nouns; I’m not sure the guidelines are strong
enough here to force the “right” outcome.

Consider this example:

// not-intended:
a.tracksWithMediaCharacteristic(b, composer: c)

…applying our guidelines:

- `tracks with media characteristic b` is a grammatical phrase

I went through a series of thoughts on this:

1. I'm not sure it is grammatical (I'll have to consult with a
   linguist).

2. I understand why it sounds natural: this is the somewhat misleading
   result of using a single-character identifier in the example. It's like
   talking about a hypothetical "person A" and "person B". You wouldn't think
   "Person Joe" was grammatical.

3. Yeah, but "umbrellas with color yellow" is perfectly fine, if
   slightly unnatural, and it has the same grammatical structure. My
   wife points out that it's like something you would say in poetry to
   make rhyme or meter work.

4. Again I need to consult with a linguist, but I now have hope that it
   actually is grammatical, meaning the whole “skipping trailing nouns
   on the base name” wrinkle can be eliminated from the guidelines.
   Thanks!

- `tracks with b` is (arguably also) a grammatical phrase

Yes, this was the interpretation for which I introduced the “skipping
trailing nouns on the base name” wrinkle.

- `b` is “part of” the "noun phrase" "tracks with media characteristic
   b"
- `b` is “part of” the “prepositional phrase” “with media characteristic b"

All true.

…and although I think this is fine under a “strict” application of the
guidelines—as `b` is part of a prepositional phrase, we *must* split
the base name after the preposition, at which point we are allowed to
“restore" the `mediaCharacteristic:` label if we wish

I don't think of it as "restoring." It's simply that the phrase you're
encoding is "tracks with media characteristic b" and b is part of a
prepositional phrase, so you put a parenthesis after the preposition:

  "tracks with (media characteristic b"

and then you apply camel case and everything falls into place.

—I don’t know in practice that the “strict” interpretation will be
what people naturally opt to use.

Well, that's the intention of the guideline. I think if we give people
examples to follow they will understand, but no matter *how* we phrase
the guidelines there's always the risk that some people will choose to
interpret them in unintended ways.

Taking it from the other direction, the way the guidelines are phrased
I worry bit that you wind up with:

// not intended
a.add(subview: v) (or add(v)) // instead of `a.addSubview(v)`

That's allowed as the guidelines were originally posted, but with the
modification to the guidelines that form B1 is to be preferred over form
B2 when both are allowed... which I swore I posted something about in
this thread in response to an excellent point someone made, but now
can't find.

Maybe I dreamt it; until I hear otherwise, *you* get credit for making
the excellent point :-)

a.add(layoutGuide: g) (or add(g)) // instead of `a.addLayoutGuide(g)`
a.add(gestureRecognizer: r) (or add(r)) // instead of
`a.addGestureRecognizer(r)`

…b/c the rule that we should ignore such trailing nouns means those
are *exactly* as-grammatical as the “intended” imports (it’s “add v”
vs “add v”, after all, under the guidelines), so at least within the
proposed guidelines there’s no apparent reason to prefer the
`addSubview` form vis-a-vis the `add(subview:)` (or `add(_)`) form(s).

Perhaps I just overlooked something from the non-label guidelines in
this response, but it’d seem the omit-needless-words would also steer
you towards `add()`, with or without a first-argument label.

It might appear to, but the words are very much needed in this case
because adding a gesture recognizer is not the same kind of operation as
adding an observer or subview.

So overall these guidelines seem more than adequate to resolve
labeling disputes if we are already dealing with "well-named”
functions but may be a bit weak to steer naming *decisions*.

The guidelines in this particular thread are just trying to deal with
the labels .

But, for guidelines, that might still be OK?

Not because they're guidelines, but because of the scope of what we're
trying to address here.

···

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

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

Thanks in advance for all your valuable input!

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

--
-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

Thanks for this Dave - I really like this document.

I had a few nits but spent a day away from it and came back and think
that this codifies much of what I’d love to see and I’d be happy
following it.

What I particularly like is that it presents APIs from the perspective of the caller.

I don’t know if the following is part of this proposal, but I think
one thing that might helpfully be discussed is how we refer to these
methods when speaking or writing about them. To me this only matters
in methods where there are argument labels - but it matters.

For example, in Objective-C, the selector would make it easy for us to
agree that the method name is moveFrom:to:. How do we refer to
moveFrom(a, to: b)? Is it the moveFrom method?

I think "moveFrom" is perfectly adequate for conversation if it's
unambiguous, but if you want to be precise it'd be "moveFrom(_:to:)".

Note that because Swift has overloading based on type, even that name is
not necessarily enough to fully disambiguate between methods.

This also helps me care less whether it is moveFrom(a, to: b) or
move(from:a, to: b) - I too prefer the second version but not enough
to object and not at all if the “to" is part of how we refer to this
method.

Thoughts? Or is that out of scope?

It's sort of tangential, but I hope I've answered your question above.

···

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

Best,

Daniel

On Feb 5, 2016, at 4: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:

* describe when and where to use argument labels
* require labels in many of the cases people have asked for them
* are understandable by humans (this means relatively simple)
* preserve important semantics communicated by existing APIs.

Please keep in mind that it is a non-goal to capture considerations we
think have a bearing on good names (such as relatedness of parameters):
it's to create simple guidelines that have the right effect in nearly
all cases.

A. When arguments can't be usefully distinguished from one another, none
  should have argument labels, e.g. min(x,y), zip(x,y,z).

B. Otherwise,

1. At the call site, a first parameter that has no argument label must
    form part of a grammatical phrase that starts with the basename, less
    any trailing nouns.

      print(x)
      a.contains(b)
      a.mergeWith(b)
      a.addGestureRecognizer(x)
           ^~~~~~~~~~~~~~~~~ trailing noun

    This phrase must have the correct semantic implications, so, e.g.

      a.dismiss(b) // no, unless a is really dismissing b
      a.dismissAnimated(b) // no, not grammatical
      a.dismiss(animated: b) // yes, using a label

2. If the first argument is part of a prepositional phrase, put the
    parenthesis immediately after the preposition.

      a.encodeWith(b)
      a.moveFrom(b, to: c)

    Thus, if words are required for any reason between the preposition
    and the first argument, they go into the first argument label.

      a.tracksWith(mediaType: b, composer: c)
      a.moveTo(x: 22, y: 99)

Notes:

a. I would recommend prepositions other than "with" in nearly all
  cases, but that's not the point of these rules.
b. I can understand the aesthetic appeal of

   a.move(from: b, to: c)

  but I believe it is not a clear enough improvement to justify
  additional complexity in the guidelines.

Questions:

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?

2. Are there any cases where you'd be confused about how to apply these
  guidelines?

Thanks in advance for all your valuable input!

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

--
-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

Looking over these guidelines again, I think I would be quite happy with them given one very simple change to Rule 2:

If the first argument is part of a prepositional phrase WITH MULTIPLE
OBJECTS, put the parenthesis immediately after the preposition.

This eliminates the need for:

a.tracksHaving(mediaType: b)

which I think is inferior to:

a.tracksHavingMediaType(b)

The downside with the latter is that it doesn't scale up. When you want
to add a second criterion, you can't just add a defaulted parameter; you
have to change the signature (breaking code) or add one or more
overloads (creating cognitive weight for users).

It's a (relatively speaking) minor issue, but IMO not glomming all that
description into the base name also results in code that's easier to
format in a balanced way, simply because there's a natural place to
break the line after the parenthesis.

On the other hand, functions like:

a.tracksWith(mediaType: b, composer: c)
a.moveTo(x: 22, y: 99)

would remain as is, because there are multiple objects for the preposition.

This also neatly solves the ‘moveFrom(a to: b)’ problem. There are two
separate prepositional phrases involved, 'from a' and 'to b', each
with a single object, so:

move(from: a to: b)

is, I believe, fully compatible with the guidelines.

I don't see anything in the guidelines I've proposed, even with your
modification, that would cause "from" to be placed inside the
parentheses. The way I read it this case still falls right into B1,
resulting in "moveFrom(a, to: b)"

···

on Sun Feb 07 2016, Charles Kissinger <swift-evolution@swift.org> wrote:

—CK

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:

* describe when and where to use argument labels
* require labels in many of the cases people have asked for them
* are understandable by humans (this means relatively simple)
* preserve important semantics communicated by existing APIs.

Please keep in mind that it is a non-goal to capture considerations we
think have a bearing on good names (such as relatedness of parameters):
it's to create simple guidelines that have the right effect in nearly
all cases.

A. When arguments can't be usefully distinguished from one another, none
  should have argument labels, e.g. min(x,y), zip(x,y,z).

B. Otherwise,

1. At the call site, a first parameter that has no argument label must
    form part of a grammatical phrase that starts with the basename, less
    any trailing nouns.

      print(x)
      a.contains(b)
      a.mergeWith(b)
      a.addGestureRecognizer(x)
           ^~~~~~~~~~~~~~~~~ trailing noun

    This phrase must have the correct semantic implications, so, e.g.

      a.dismiss(b) // no, unless a is really dismissing b
      a.dismissAnimated(b) // no, not grammatical
      a.dismiss(animated: b) // yes, using a label

2. If the first argument is part of a prepositional phrase, put the
    parenthesis immediately after the preposition.

      a.encodeWith(b)
      a.moveFrom(b, to: c)

    Thus, if words are required for any reason between the preposition
    and the first argument, they go into the first argument label.

      a.tracksWith(mediaType: b, composer: c)
      a.moveTo(x: 22, y: 99)

Notes:

a. I would recommend prepositions other than "with" in nearly all
  cases, but that's not the point of these rules.
b. I can understand the aesthetic appeal of

   a.move(from: b, to: c)

  but I believe it is not a clear enough improvement to justify
  additional complexity in the guidelines.

Questions:

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?

2. Are there any cases where you'd be confused about how to apply these
  guidelines?

Thanks in advance for all your valuable input!

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

--
-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

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,

···

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.

--
-Dave

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, 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 win, but I certainly don’t mind it.

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

Forgive my barbaric removal of external labels. It should be `createAttribute(tag: t, data: d)`. Alternatively, it could be `attributeFor(tag: t, data: d)` — I could go either way.

(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:)`. 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.)

  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 `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

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

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?

  - Doug

···

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 <mailto:dgregor@apple.com>> wrote:

On Feb 2, 2016, at 10:17 PM, Jonathan Tang via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
On Tue, Feb 2, 2016 at 4:32 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

The “sentence” requirement of Rule 1 doesn’t seem to be met here, but
the “primary semantics” part would still apply?

I don't know what you mean; AFAICT, both parts apply; you have to apply
them together. You omit the argument label if and only if you can
describe the primary semantics with a complete sentence starting in the
base name and ending in the first argument. The primary semantics of
the method don't end with the mediaType: the genre is as much part of
the primary semantics, so we use an argument label.

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.

···

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

On Feb 3, 2016, at 2:06 PM, Dave Abrahams via swift-evolution >> <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

I think this one:

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

reads better than this one:
  
  a.tracksHaving(mediaType: “Wax Cylinder”, andPianist: “Brahms”)

With the rationale that it would sound kind of stilted if you tried to say it out loud, and that splitting apart “tracksHavingMediaType” into “tracksHaving”… “mediaType” seems like a fairly random choice. It could just as well be “tracks”…”havingMediaType”, where the first option only introduces the split when it must happen, at the first parameter.

I haven’t thought enough about it to see a general pattern, but that’s what I think on this particular example. HTH.

Matt

···

On Feb 3, 2016, at 14:34, Dave Abrahams via swift-evolution <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> 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