简体   繁体   English

Swift推断闭包参数之谜

[英]Swift inferred closure parameter puzzle

As a homage to Ruby I've been playing with an extension to Int that allows me to write useful code like this: 为了向Ruby致敬,我一直在玩Int的扩展,该扩展允许我编写如下有用的代码:

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

This works well and here's the extension: 这很好用,这是扩展名:

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

Now, I'd like to pass the iteration number in to the closure, so I added a 2nd times() function to extension: 现在,我想将迭代编号传递给闭包,因此我向扩展添加了2nd times()函数:

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

Which can be invoked like this: 可以这样调用:

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

Now, according to the Swift docs, 现在,根据Swift文档,

It is always possible to infer the parameter types and return type when passing a closure to a function as an inline closure expression. 将闭包作为内联闭包表达式传递给函数时,总是可以推断参数类型和返回类型。 As a result, you never need to write an inline closure in its fullest form when the closure is used a function argument. 因此,当闭包用作函数参数时,您无需编写其完整形式的内联闭包。

That sounds great, because then I can omit the parameter and return types, ie (i: Int) -> () , and just use the following syntax instead: 这听起来不错,因为这样我就可以省略参数和返回类型,即(i: Int) -> () ,而只需使用以下语法即可:

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

But this leads to the following error: Error: Ambiguous use of 'times' 但这会导致以下错误: Error: Ambiguous use of 'times'

Is this way of invoking my times() function really ambigous to the compiler? 调用我的times()函数的这种方式真的对编译器来说模棱两可的吗?

It is ambiguous. 这是模棱两可的。 Both .times() methods can be used with the given closure expression if the parameter type of the closure is not known. 如果闭包的参数类型未知,则两种.times()方法都可以与给定的闭包表达式一起使用。

If you just write { i in println("Year \\(i)") } , it is just a closure that takes one parameter of any type. 如果您仅将{ i in println("Year \\(i)") }编写,则它只是一个闭包,它接受任何类型的一个参数。 Well, EVERY function type in Swift can be viewed as taking one parameter: 好吧,Swift中的每个函数类型都可以看作是一个参数:

  • What you think of as zero-parameter functions actually take one parameter of type () (aka Void ), of value () , that's why the type is written as () -> something 您所认为的零参数函数实际上采用了() (aka Void )类型的一个值() ,这就是为什么将该类型写为() -> something
  • What you think of as multiple-parameter functions actually take one parameter of tuple type, a tuple of all the "multiple arguments", that's why the type is written as (foo, bar) -> something . 您认为多参数函数实际上采用了一个元组类型的参数,即所有“多个参数”的元组,这就是为什么将类型写为(foo, bar) -> something

So, basically, your closure expression, without specifying the type of i , can be inferred to ANY function type in Swift that returns Void . 因此,基本上,您的闭包表达式(无需指定i的类型)可以推断为Swift中返回Void任何函数类型。 The functions taken by both .times() methods match this -- for the first .times() method, it is inferred as a function of type () -> () , ie i has type () ; 这两个.times()方法采用的函数都与此匹配-对于第一个.times()方法,将其推断为type () -> ()的函数,即i具有type () ; for the second .times() method, it is inferred as a function of type Int -> () , ie i has type Int . 对于第二个.times()方法,将其推断为Int -> ()类型的函数,即, i具有Int类型。

it looks like the ambiguity derives from the 2 extension methods having the same name. 看起来模棱两可是从具有相同名称的2个扩展方法得出的。 If you comment your first version of the times function, it works fine - if you comment the 2nd instead, surprisingly, you do not get an error from the compiler. 如果您对times函数的第一个版本进行注释,则可以很好地工作-如果您对第二个函数进行注释,则令人惊讶的是,您不会从编译器中得到任何错误。

There is no ambiguity in my opinion, because the 2 functions have different signature - () -> () is different than (iteration: Int) -> () . 我认为没有歧义,因为这两个函数具有不同的签名- () -> ()(iteration: Int) -> () I think it's a bug in the compiler, specifically type inference is failing. 我认为这是编译器中的错误,特别是类型推断失败。

Making the call explicit instead it works fine 明确地进行呼叫可以正常工作

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

If the first version with the parameterless closure is commented, the line above compiles correctly, if instead the 2nd overload is commented, compilation fails as expected. 如果注释了第一个带有无参数闭包的版本,则上面的行将正确编译,如果注释了第二个重载,则编译将按预期失败。

To prove that something is wrong in the compiler, this seems to work, whereas I'd expect a compilation error instead: 为了证明编译器出了点问题,这似乎可以解决问题,但是我希望编译错误代替:

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

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

This is the output from the playground console: 这是游乐场控制台的输出:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM