为什么我们可以在闭包的类型注释中使用“内部参数标签”,当它们(貌似)永远无法访问时?

时间:2017-03-06 23:13:17

标签: swift closures

背景

这自然是合法的:

let closure: (Int, Int) -> () = { print($0 + $1) }
closure(1, 2) // 3
然而,自实施进化提案以来

在Swift 3中,以下内容不合法:

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

错误:函数类型可能没有参数标签a,而是使用_

这是预期的,引自SE-0111:

  

函数类型只能根据形式的类型来定义   参数和返回值。

然而,奇怪的是(并且由上面的错误消息提示),这是合法的:

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

但是,据我所知,我们不能使用上面的ab(它们甚至不应该被允许,因为它们不属于参数?)。

问题

  • 上面的最终代码段有合理的理由吗?我们可以使用或访问“内部参数标签”ab(给出引用,我们不应该......),或者这可能是对SE-0111实施的疏忽?

2 个答案:

答案 0 :(得分:4)

如果你删除了为参数命名的能力,那么你就会失去一个重要的人际交流方面,告诉未来的维护者或用户这个代码的目的,这些都是抱怨,而不是无理由的。这些参数。好吧,就外部参数名称而言, 的能力已被删除。但是通过使用内部参数名称让它打开以隐藏在编译器之外,我们至少可以恢复某些通信。

但是,我们无法恢复该通信的 ,因为内部参数未显示在代码完成中:

enter image description here

这被批评为新政权在这个问题上的一个缺陷,在我看来,这是正确的。

答案 1 :(得分:4)

您正在观察的是能够定义纯粹的化妆品"闭包类型的参数标签。如SE-0111所述,这被in the rationale视为一部分:

  

为响应社群反馈,核心团队正在接受该提案,其修订版允许在文档的封闭类型中使用“纯粹化妆品”参数标签(如the alternatives section中所述)。

在要求参数标签为_的提案之后,这些装饰参数标签的语法已更改,以便明确表示化妆品标签未在呼叫站点使用。详情见an additional commentary

  

核心团队要求SE-0111的具体修订是   应该要求所有“化妆品”标签都包含API名称_。   例如,不允许这样做:

var op : (lhs : Int, rhs : Int) -> Int
     

相反,它应拼写为:

var op : (_ lhs : Int, _ rhs : Int) -> Int

虽然实际上,实际上,这使得化妆品标签相当无用,因为它们不会出现在呼叫现场甚至是自动完成 - 只是在实际的声明本身。因此,他们自我记录的意图有点丢失。

Swift团队意识到了这个缺点,并希望在Swift 3之后进行纯粹的附加改变,以纠正这种情况。

这里是他们提出的草图(再次,在the additional commentary中):

  

首先,我们扩展变量,属性和参数的声明名称,以允许参数名称作为其声明名称的一部分。例如:

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
     

这将恢复表达闭包概念的能力   不带标签的参数,作为其声明的一部分   要求参数标签成为类型系统的一部分(允许,   例如运算符+被传递到需要的东西   参数标签)。

     

其次,扩展函数类型的规则以允许参数API   标签当且仅当将它们用作声明的类型时   允许参数标签,并将其解释为糖形式   在基础声明上提供这些标签。这意味着   上面的例子拼写为:

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.

这个提议的解决方案很好地允许在呼叫站点使用标签,允许自我记录参数标签,而不会使类型系统与它们复杂化。另外(在大多数情况下)它允许在闭包的参数类型旁边写标签的表达语法 - 我们习惯于在Swift 3之前做。