Named parameters - why hidden first?/proposal interest

Reply below.

but what I found was that my APIs generally fell in line naturally with the defaults.
And when they didn't, looking for a way to take advantage of the defaults often
led to a better API

I'm not sure I follow this. A change would result in:

Pros:
- the rule of "least surprise"
- developers can edit function definitions more quickly

Cons:
- one extra character (i.e.: from "foo: Int" to "_ foo: Int")
- one missed opportunity to set an example of "good style"

Am I missing something?

One extra character?

With our current Objective-C style, we need to repeat at least one word and often need to include "with", "by", "using", etc. to make it read nicely.

Compare:

1.
splitWithSeparator(_ separator: ...

2.
split(separator: ...

The first form repeats "separator" twice and needs "with". And then there's (as you mentioned) the underscore and the space character. In short, tons of crap.

R+

···

Sent from my iPhone

On 24 Jan 2016, at 00:06, Charles Constant via swift-evolution <swift-evolution@swift.org> wrote:

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

Sorry, I thought you were suggesting that if an external name were not specified, the internal name would be optionally allowed at the call site. I see now that you were proposing to first make all parameters have external names by default, and then to make the label optional.

I still disagree with this, but it doesn't break anything. :-)

Jordan

···

On Jan 23, 2016, at 6:02, Tino Heth <2th@gmx.de> wrote:

If the library author decides to change the internal name, it's now a source-breaking change for clients. (Alternately, all the existing internal names are now external names, without any thought given to them, which would be just as bad.)

Imho this is no good argument — you can extend it to ban all labels.

No, that's not the case. External names are part of the method name and signature and are part of the source and binary interface for a library. Internal names are local variables with a little bit of documentation. Every parameter has both internal and external names; it's just that the logic for what happens when you only specify one that's different.

I'm not sure if I understand this completely:
When each parameter has an external and an internal name, changing the latter should never be an issue for users of that method… where's the fault in my logic?
The "only"* breaking change I see happens when the behavior for all parameters is unified, and suddenly parameters without external name (or "empty external name"...) would receive one.

I think that the simple rule that all parameters, wether in init or a method have named parameters. I.E. I like:

    say(message: “hello”)

Since to me it is clear.

I also note there is considerable discussion in the API guidelines about how to qualify method names particularly when type is vey general like Any, e.g. append:

    anyArray.append([1, 2, 3])

Does this append the single element [1, 2, 3] (an array) or does it append three separate elements, 1, 2, and 3? If the argument is labeled then it is clear:

    anyArray.append(element: [1, 2, 3]) // append with a single argument
    anyArray.append(elements: 1, 2, 3) // append with var arts

···

Sent from my iPad

On 25 Jan 2016, at 12:46 PM, Charles Srstka via swift-evolution <swift-evolution@swift.org> wrote:

On Jan 23, 2016, at 5:34 AM, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:

on Fri Jan 22 2016, Charles Srstka <swift-evolution@swift.org> wrote:

On Jan 21, 2016, at 1:25 PM, Tony Parker via swift-evolution >>>> <swift-evolution@swift.org> wrote:

We surveyed the entire surface area of the iOS and OS X SDKs,
looking to see how many arguments typical methods had. We found that

the vast majority of methods actually have just one argument. If we
went with a rule that first arguments should be named, then methods
like:

sayMessage(“hello”)

would be this instead:

say(message: “hello”)

which isn’t really much of an improvement, in my mind.

How about making it so that methods with just one argument have no
label on the argument by default, but methods with two or more
arguments have labels on all arguments? This would prevent what you
describe above, while eliminating the ugliness of one parameter being
treated differently from all the rest in the same method.

...and replace it with the ugliness of one parameter being treated
differently from two. It would also force me to write "_" explicitly on
the first parameter of all of my 2+ parameter methods. Yes, it's a
little bit weird, but when optimizing APIs for readability at the call
site, the defaults we have actually work in practice.

I’m playing devil’s advocate a bit, here—but is being forced to write “_” on a parameter any more onerous than being forced to write the entire label name out twice?

Charles

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

Excuse me if this has been mentioned already. I just subscribed to this list and don’t know anything about how to use it.

It seems to me that there is a third choice for parameter names, which has yet to be explored. The way protocols are implemented now can educate us about it:

protocol Protocol {
   // This argument label does nothing,
   // but is required for compilation.
   func function(int: Int)
}

// This compiles.
struct Struct: Protocol {
   func function(not_int_atAll: Int) {}
}

Because internal parameter names in protocol names do nothing, they should not be required:
protocol Protocol {
   func function(Int)
}

This inspires the following syntax:
struct Struct: Protocol {
   func function(Int) {
      // The parameter is called $0 here
   }
}

This kind of thing is sorely needed for operators, which use the standards “lhs” and “rhs” despite no hands being involved.

struct Struct: Protocol {
   // Should also be valid;
   // but we should explicitly have to
   // opt out of external naming.
   func function(_ int: Int) {}
}
struct Struct: Protocol {
   // Should not be valid;
   // int is now the external parameter name
   func function(int: Int) {}
}
struct Struct: Protocol {
   // Should not be valid.
   func function(preposition int: Int) {}
}

···

On Jan 21, 2016, at 5:04 PM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

On Jan 21, 2016, at 1:58 PM, John McCall via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jan 21, 2016, at 12:33 PM, Charles Constant via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

say("hello")

is a big improvement, which IMO perfectly illustrates why we want the
default as it is.

Maybe so, but there just has to be some way to do this that doesn't bake the design into the entirety of the Swift language. Is there some way Apple could implement this that only affects Swift imports/exports with Obj-C?

You’re repeating your assumption here that the current design was reached by necessity for ObjC importing. That is not the case. As has been said multiple times in this very thread, we looked at a lot of existing APIs — and of course those APIs included ObjC APIs, but not exclusively by any stretch — and decided that, far more often than not, the first argument of a function/method is a “direct object” whose relationship to the operation is obvious and does not benefit from labelling. We strongly considered adopting the rule that all arguments should be labelled by default, and we decided that, on balance, it led to a lot of really useless labels. It’s a very simple and understandable language rule, but it leads to worse results when you actually apply to it to real code. There are, of course, exceptions, but there would have been in any case.

But like Dave said, we’re about to have a major conversation about this, and you are welcome to participate.

One thing to consider is, if you follow either Cocoa's contemporary naming conventions or the proposed Swift 3 naming conventions, then the labels you end up with for secondary arguments frequently aren't good binding names, so the conventions steer you toward providing an explicit binding name:

// Classic Cocoa style
func doStuff(stuff: Stuff, involvingThing thing: Thing) { ... }
// Swift 3 style
func do(stuff: Stuff, involving thing: Thing) { ... }

We could make the language rules more consistent, while still encouraging people to follow the naming guidelines, by *requiring* every argument after the first to specify both an argument label (or explicit '_') and binding name. That makes the naive 'min(x: Int, y: Int)' case an error we can provide an easy fixit for.

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

With our current Objective-C style, we need to repeat at least one word

and often need to include "with", "by", "using", etc. to make it read nicely

When it comes to my own code, I agree with you 100%. However, if I
understand the Swift team's rationale, using a label for the first argument
is more often than not, bad form.

The point I was trying to make with the pros/cons bullet points is that,
even being charitable with the cons and understated about the pros, the
change still makes more sense.

As another member of the “anti-fan-club” for the unnamed first parameter, I have to ask: what would be wrong with:

- single-argument functions: default to unnamed “first” (really: only) parameter
- multi-argument functions: default to all-named parameters

…(other than the obvious inconsistencies/special-casing so introduced)?

I mean more in terms of design goals/readability concerns.

The API guidelines seem to address two scenarios for multi-argument functions:

- functions in which one argument is the "semantic focus” (and thus deserves to be the first argument, with its description folded-into the method name)
- functions in which all arguments are “co-equal participants” and have *identical* semantic roles, in which case all parameters should go un-named (e.g. ==, zip, etc.)

…but don’t address a third scenario, wherein:

- there are multiple arguments
- no one argument is the obvious, natural “semantic focus” of the method (thus there’s no natural candidate for which argument’s label ought to get folded-into the method name)
- the arguments all have distinct logical-roles within the method (thus the all-unnamed approach is not great)

…which scenario I think is not all that uncommon in application code (anecdotally 15-30%, depending on problem domain)?

As an example, consider something like this:

protocol PresentationController {
  typealias Content
  typealias PresentationUpdate

  // one of these feels right:
  func inferPresentationUpdate(fromPrevious previous: Content, toCurrent current: Content) -> PresentationUpdate
  func inferPresentationUpdate(from previous: Content, to current: Content) -> PresentationUpdate

  // none of these feels right:
  func inferPresentationUpdateFrom(content: Content, toCurrent current: Content) -> PresentationUpdate
  func inferPresentationUpdateFrom(content: Content, to current: Content) -> PresentationUpdate
  func inferPresentationUpdateFromPrevious(content: Content, toCurrent current: Content) -> PresentationUpdate
  func inferPresentationUpdateFromPrevious(content: Content, toCurrent current: Content) -> PresentationUpdate

  // nor do any of these:
  func inferPresentationUpdateTo(content: Content, fromPrevious previous: Content) -> PresentationUpdate
  func inferPresentationUpdateTo(content: Content, from previous: Content) -> PresentationUpdate
  func inferPresentationUpdateToCurrent(content: Content, fromPrevious previous: Content) -> PresentationUpdate
  func inferPresentationUpdateToCurrent(content: Content, from previous: Content) -> PresentationUpdate

}

…which is as always a bit contrived, but illustrates a scenario wherein neither parameter is arguably the natural "semantic focus” of the method.

(If you need to be convinced this shouldn’t be a method on `Content`, consider that `Content` may be a simple model entity, and that different types of “presentations” may differ in how they want to present model *updates* to the user).

But that’s my 2c; I don’t like the inconsistency, I understand the stated reasons for it, but I think multi-argument methods w/out an obvious “semantic focus” are common enough to deserve consideration.

···

On Jan 24, 2016, at 6:32 AM, Charles Constant via swift-evolution <swift-evolution@swift.org> wrote:

> With our current Objective-C style, we need to repeat at least one word and often need to include "with", "by", "using", etc. to make it read nicely

When it comes to my own code, I agree with you 100%. However, if I understand the Swift team's rationale, using a label for the first argument is more often than not, bad form.

The point I was trying to make with the pros/cons bullet points is that, even being charitable with the cons and understated about the pros, the change still makes more sense.

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

Why not use split(withSeparator separator) instead?

The external parameter name still completes the natural language style, while allowing other split functions to be grouped together under a single action name (split). Personally I think is the best of both, in essence when you’re using the function/method you’re defining the action you want (split) then how you want to do it (withSeparator) which seems nice and logical.

- Haravikk

···

On 24 Jan 2016, at 10:34, Rudolf Adamkovic via swift-evolution <swift-evolution@swift.org> wrote:

With our current Objective-C style, we need to repeat at least one word and often need to include "with", "by", "using", etc. to make it read nicely.

Compare:

1.
splitWithSeparator(_ separator: ...

2.
split(separator: ...

The first form repeats "separator" twice and needs "with". And then there's (as you mentioned) the underscore and the space character. In short, tons of crap.

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

We’re going to be discussing this all again very soon; let’s just wait for that thread.

John.

···

On Jan 21, 2016, at 2:49 PM, Jessy Catterwaul <mr.jessy@gmail.com> wrote:
Excuse me if this has been mentioned already. I just subscribed to this list and don’t know anything about how to use it.