Why may we use “internal argument labels” in type annotations of closures, when they (seemingly) can never be accessed?

女生的网名这么多〃 提交于 2019-12-01 22:37:01

问题


Background

This is naturally legal:

let closure: (Int, Int) -> () = { print($0 + $1) }
closure(1, 2) // 3

Whereas, since the implementation of evolution proposal

  • SE-0111: Remove type system significance of function argument labels

in Swift 3, the following is not legal:

let closure: (a: Int, b: Int) -> () = { /* ... */ }

Error: function types may not have argument label a, use _ instead.

Which is expected, as, quoting from SE-0111:

Function types may only be defined in terms of the types of the formal parameters and the return value.

Curiously, however (and as prompted by the error message above), this is legal:

let closure: (_ a: Int, _ b: Int) -> () = { print($0 + $1) }
closure(1, 2) // 3

However, as far as I can tell, we can't ever make use of a and b above (they shouldn't even be allowed, as they are not part of the types of the parameters?).

Question

  • Is there any reason for the final code snippet above to be legal? Can we make use or access the "internal argument labels" a and b (given the quote, we shouldn't ...), or is this possibly an oversight in the implementation of SE-0111?

回答1:


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.




回答2:


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.



来源:https://stackoverflow.com/questions/42637340/why-may-we-use-internal-argument-labels-in-type-annotations-of-closures-when

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!