This is naturally legal:
let closure: (Int, Int) -> () = { print($0 + $1) }
closure(1, 2) // 3
Whereas, since the i
There have been complaints, not unreasonable, that if you remove the ability to name the parameters, you lose an important aspect of the human communication that tells a future maintainer or user of this code the purpose of these parameters. Well, that ability has been removed as far as external parameter names are concerned. But by leaving it open to sneak past the compiler by using just internal parameter names, we recover at least something of that communication.
However, we do not recover enough of that communication, because the internal parameters don't show up in code completion:
This has been criticized as a flaw in the new regime on this matter, and, in my opinion, rightly so.
What you're observing is the ability to define "purely cosmetic" parameter labels for closure types. This was accepted as a part of SE-0111, as stated in the rationale:
In response to community feedback, the core team is accepting the proposal with a revision to allow “purely cosmetic” parameter labels in closure types for documentation (as outlined in the alternatives section).
The syntax for these cosmetic parameter labels changed after the proposal to require an argument label of _
, in order to make it explicit that the cosmetic labels aren't used at the call-site. This was detailed in an additional commentary:
The specific revision requested by the core team to SE-0111 is that all “cosmetic” labels should be required to include an API name of
_
. For example, this would not be allowed:var op : (lhs : Int, rhs : Int) -> Int
instead, it should be spelled as:
var op : (_ lhs : Int, _ rhs : Int) -> Int
Although really, in practice, this makes the cosmetic labels fairly useless, as they don't show up at the call-site or even in auto-completion – only at the actual declaration itself. Therefore their intent to be self-documenting is somewhat lost.
The Swift team are aware of this shortcoming, and will be looking to make a purely additive change post-Swift 3 in order to rectify the situation.
Here's the sketch that they proposed (again, in the additional commentary):
First, we extend declaration names for variables, properties, and parameters to allow parameter names as part of their declaration name. For example:
var op(lhs:,rhs:) : (Int, Int) -> Int // variable or property. x = op(lhs: 1, rhs: 2) // use of the variable or property. // API name of parameter is “opToUse”, internal name is "op(lhs:,rhs:)”. func foo(opToUse op(lhs:,rhs:) : (Int, Int) -> Int) { x = op(lhs: 1, rhs: 2) // use of the parameter } foo(opToUse: +) // call of the function
This will restore the ability to express the idea of a closure parameter that carries labels as part of its declaration, without requiring parameter labels to be part of the type system (allowing, e.g. the operator + to be passed into something that requires parameter labels).
Second, extend the rules for function types to allow parameter API labels if and only if they are used as the type of a declaration that allows parameter labels, and interpret them as a sugar form for providing those labels on the underlying declaration. This means that the example above could be spelled as:
var op : (lhs: Int, rhs: Int) -> Int // Nice declaration syntax x = op(lhs: 1, rhs: 2) // Same as above // API name of parameter is “opToUse”, internal name is "op(lhs:,rhs:)”. func foo(opToUse op : (lhs: Int, rhs: Int) -> Int) { x = op(lhs: 1, rhs: 2) // Same as above. } foo(opToUse: +) // Same as above.
This proposed solution quite nicely allows the labels to be used at the call-site, allowing for self-documenting parameter labels, while not complicating the type-system with them. Additionally (in most cases) it allows for the expressive syntax of writing the labels next to the parameter types of the closure – which we were used to doing pre-Swift 3.