Require parameter names when referencing to functions

Correct me if I‘m wrong but isn‘t this the current behavior? If there is no overloads but the function has parameter labels it would create an issue when the user only uses the base name and if in the future one would introduce another member with the same base name such as the count example.

I think the most appropriate solution is what Jordan mentioned in the end of his post.

I'm in favour of this change for functions with more than one parameter in theory, as it always seemed as weird that Swift allowed it.

But is true that having to write init(_:) or max(_:_:) when it's clear would make Swift code uglier that it is now.

I also think that Nevin point is really important:

that change is still causing trouble in today's Swift. So I hope we can make sure we're not causing any unwanted side effects.

Ultimately I think I would support an implementation like Jordan's suggestion:

Would a change like this impact trailing closure syntax? Requiring items.first(where:) { $0.isHidden } versus items.first { $0.isHidden } would not be very nice.


This is an already existing problem though. The difference with today is there's some chance the compiler will automatically switch to the new function and not tell you. This is probably fine if the labels are the same since the functions will have been designed for overloading. If the label differs that's problematic and a good thing the compiler tells you.

Another source incompatibility when adding overloads: minimum version requirements. If a new better-matching overload with the same labels appears but requires wrapping into an if #available block, that is source-breaking too:

func test(_ a: Any) {}

@available(macOS 10.99, *)
func test(_ a: Int) {}

test(1) // error: 'test' is only available on OS X 10.99 or newer

Yet, we don't force all call sites to be written like test(1 as Any) just in case a new overload appears in the future. That'd be madness. My feelings are similar towards the labels: they should be optional when there's no risk of confusion.

This non-specificity is especially useful in your own code where you have a tendency to refactor things and refine the types & labels used as you improve things in your code.

Perhaps a solution could be a warning (with a fixit) about not specifying the labels when referring to functions across module boundaries.

Like default parameters, trailing closure syntax is a call-site concern, which (I hope/assume) would be unaffected by this pitch.

Yet along a related axis, we don’t allow functions to be called with or without argument labels as desired by a client—they are considered a necessary part of the name of the function for the purpose of calling, so why should referencing be any different? That, to me, is a closer parallel than adding a purely type-based overload versus an “overload” based on argument labels. A library author may falsely believe they are safely adding this overload since it has a different argument label than existing methods, without realizing that that they may break clients which are referencing the function (or even if they are aware of the issue, they have no way to address it without changing the base name).

This is also why I’m not super compelled by the “non-specificity is especially useful in your own code” argument. Why wouldn’t the same non-specificity be useful at the call site? You already break all those usages when you change the argument label, so this change doesn’t seem like it would introduce significantly more code churn than already exists today while API details are still in flux.


For the record, a few people have called this "my" proposal, but @cukr was first to suggest it, and several others had already agreed before I posted.


Not sure what the answer to your question is, but I would personally view this as a feature, not a bug. Trailing closure syntax dropping parameter labels was a mistake IMO.


The answer is no. Trailing closure syntax is part of call syntax, not referencing syntax. That doesn't mean it can't be revisited later, but it's not tied to this discussion.


Thanks for clarifying, that's what I guessed would be the case but wanted to double-check.

With that in mind, I think Jordan's 'middle ground' strategy is acceptable — argument labels have semantic value, but it seems silly to need to write _ (one or more times) when all arguments are unnamed.

1 Like

We do have support for this in code-completion, but it could use some love. Currently it only works on non-generic function types.


This example backfires on itself. Let's consider an example that's very close to yours, but without the "objectionable" omission of parameter keywords:

Here's the corresponding list (culled from your greater list):

init(_ c: Character)
init(_ cocoaString: NSString)
init(_ content: Substring.UnicodeScalarView)
init(_ scalar: Unicode.Scalar)
init(_ substring: __shared Substring)
init(_ unicodeScalars: String.UnicodeScalarView)
init(_ utf16: String.UTF16View)
init(_ utf8: String.UTF8View)
init(_ cocoaString: AnyObject)
init(_ sel: Selector)

You can soak in that list too, and realize that the problem you're pointing out is as much about the inscrutability of type inference as it is about the ambiguity of omitting the labels.


You say that as if it's a settled matter that this is problematic. @jrose suggested that this is a problem, but didn't provide any justification for why it's "harder".

It seems arguable that this is no harder than (say) changing the spelling of a keyword on an existing method. In both cases, the outcome is a compilation error or warning that didn't occur previously. In both cases, the library author may consider whether the upheaval is worth the benefits of the change. But it's not obvious that one change is "harder" than the other.

(This is under the assumption that adding the second method does in fact produce a compilation error.)

1 Like

Your list is incorrect. init(_cocoaString:) and init(_sel:) have labels with leading underscores, not unlabeled parameters; you also missed init?(_ codeUnits: Substring.UTF16View) and init?(_ codeUnits: Substring.UTF8View) from later in the list. That leaves:

init(_ c: Character)
init(_ cocoaString: NSString)
init(_ content: Substring.UnicodeScalarView)
init(_ scalar: Unicode.Scalar)
init(_ substring: __shared Substring)
init(_ unicodeScalars: String.UnicodeScalarView)
init(_ utf16: String.UTF16View)
init(_ utf8: String.UTF8View)
init?(_ codeUnits: Substring.UTF16View)
init?(_ codeUnits: Substring.UTF8View)

This is a list I'm quite comfortable with. Every overload here straightforwardly converts something that's already very string-like to a string. None of these overloads are implementation details or security risks.


That's true, but not the "harder" I was getting at. I probably should have phrased it as "no easier than today" though.

I think people understand and agree that you shouldn't change the name of an existing API, at least not without thinking carefully about it. And people understand and agree that it should be okay to add a new API with a new name. So the murkier areas are

  1. adding a new overload-by-type to an existing API, which should be considered carefully, and which this proposal does not really make any more or less safe

  2. adding a new API that has the same base name as an existing API but different argument labels (ignoring default arguments for the time being)

I am pretty strongly of the opinion that (2) should be as safe as adding a new API that has a different base name, and that's what this proposal addresses.


I'm very much in favor of this. I've run into this issue many times when naming a function succinctly but I have a property that starts with the same name. It's really frustrating and makes me have to compromise my method name or property name. I think the requirement to specify the method fully is more precise as stated and resolves all of these issues. Big +1 from me.


I was initially +1 on this, but reading through the negatives I'm a firm -1. It would be a huge breaking change to existing code, and it ruins elegant syntax like

I really wish we could solve problems like let foo = foo(arg), but it seems like that can be tackled by improvements to type resolution, without requiring any further disambiguation from the user (it's already unambiguous).

1 Like

I didn't though about it, but would that rule also apply to operators ?
Would it still be possible to write foo.reduce(0, +)

They don't really seem to suffer from the same ambiguity problem, since you can't use operator symbols as a variables.

1 Like

I'm fine with it. And the author of the method has full control - if they want, they can call it firstWhere instead - nothing in the language precludes it. My understanding is that Swift simply choses to, by convention, express method similarity more explicitly than merely a common string prefix, but with also with an explicit '(' terminator on that prefix.

There already has to be some way to disambiguate e.g. first(where:) and first(after:) - the compiler can't implicitly refer to both as merely first. Disambiguating consistently seems like a readability win to me (and lessens vulnerability to API changes).

(granted this hypothetical first(after:) is fairly esoteric, but it's technically valid, functionally distinct, and cannot be disambiguated based on parameter type, which is (T) -> Bool in both)

That all said, if the compiler could disambiguate based on the presence of the trailing closure - between a single-parameter first(where:) and a zero-parameter first - that'd be an interesting avenue. Though it seems like that could be ambiguous when used in an if statement.

Terms of Service

Privacy Policy

Cookie Policy