Swift推断闭包参数拼图

时间:2014-09-06 06:44:07

标签: ios ruby swift

作为对Ruby的致敬,我一直在使用Int的扩展,允许我编写这样有用的代码:

3.times { println("I keep holding on") }

这很好用,这是扩展名:

extension Int {
    func times(fn: () -> ()) {
        for i in 1...self {
            fn()
        }
    }
}

现在,我想将迭代号传递给闭包,所以我在扩展中添加了第二次()函数:

extension Int {
    func times(fn: (iteration: Int) -> ()) {
        for i in 1...self {
            fn(iteration: i)
        }
    }
}

可以这样调用:

5.times { (i: Int) -> () in println("Year \(i)") }

现在,根据Swift文档,

  

始终可以推断参数类型和返回类型   将闭包传递给函数作为内联闭包表达式时。   因此,您永远不需要完整地编写内联闭包   当闭包使用函数参数时形成。

听起来不错,因为我可以省略参数和返回类型,即(i: Int) -> (),只需使用以下语法:

5.times { i in println("Year \(i)") }

但是这会导致以下错误:Error: Ambiguous use of 'times'

这种方式是调用我的times()函数真的对编译器不明显吗?

2 个答案:

答案 0 :(得分:3)

这是模棱两可的。如果闭包的参数类型未知,则.times()方法都可以与给定的闭包表达式一起使用。

如果你只是写{ i in println("Year \(i)") },它只是一个闭包,它接受任何类型的一个参数。好吧,Swift中的每个函数类型都可以看作是一个参数:

  • 您认为零参数函数实际上采用()类型的一个参数(又名Void),值为(),这就是为什么类型被写为{{1 }}
  • 您认为多参数函数实际上采用了元组类型的一个参数,即所有“多个参数”的元组,这就是为什么类型被写为() -> something

因此,基本上,您的闭包表达式(不指定(foo, bar) -> something的类型)可以推断为返回i的Swift中的任何函数类型。两个Void方法采用的函数都匹配 - 对于第一个.times()方法,它被推断为类型.times()的函数,即() -> ()具有类型{{1 }};对于第二个i方法,它被推断为类型()的函数,即.times()具有类型Int -> ()

答案 1 :(得分:2)

看起来模糊性来自具有相同名称的2种扩展方法。如果您对times函数的第一个版本发表评论,它可以正常工作 - 如果您对第二个版本进行评论,那么,令人惊讶的是,会从编译器中获取错误。

我认为没有歧义,因为2个函数具有不同的签名 - () -> ()(iteration: Int) -> ()不同。我认为它是编译器中的一个错误,特别是类型推断失败。

使呼叫显式而不是正常

5.times { (i: Int) -> () in println("Year \(i)") }

如果注释了无参数闭包的第一个版本,则上面的行正确编译,如果第二个重载被注释,则编译按预期失败。

为了证明编译器出了问题,这似乎有效,而我反对编译错误:

extension Int {
    func times(fn: () -> ()) {
        for i in 1...self {
            fn()
        }
    }
}

5.times { i in println("Year \(i)") }

这是游乐场控制台的输出:

Year ()
Year ()
Year ()
Year ()
Year ()