为什么Swift编译器不能推断出这个闭包的类型?

时间:2017-03-01 14:22:35

标签: swift type-inference

所以我编写代码来区分我的应用程序的多个版本:

static var jsonURLNL =  {
    if ProcessInfo.processInfo.environment["CONSUMER"] != nil {
        return URL(string: "consumerURL")!
    }
    return URL(string: "professionalURL")!
}()

但是我遇到了编译错误:

  

无法推断出复杂的闭包返回类型;添加显式类型以消除歧义

为什么Swift编译器不能知道这会返回URL?我认为在这种情况下这是相当明显的。

我对这个问题的目标不是对Xcode或Swift进行批评,而是增加我对编译器如何在Swift中推断类型的了解。

2 个答案:

答案 0 :(得分:9)

只有在关闭时才会自动推断闭包的返回类型 闭包由单个表达式组成,例如:

static var jsonURLNL =  { return URL(string: "professionalURL")! }()

或者是否可以从调用上下文推断出类型:

static var jsonURLNL: URL =  {
    if ProcessInfo.processInfo.environment["CONSUMER"] != nil {
        return URL(string: "consumerURL")!
    }
    return URL(string: "professionalURL")!
}()

static var jsonURLNL = {
    if ProcessInfo.processInfo.environment["CONSUMER"] != nil {
        return URL(string: "consumerURL")!
    }
    return URL(string: "professionalURL")!
}() as URL

简化示例:此单表达式闭包编译:

let cl1 = { return 42 }

但是这个多表达式闭包没有:

let cl2 = { print("Hello"); return 42 }
// error: unable to infer complex closure return type; add explicit type to disambiguate

以下几行编译,因为从上下文推断出类型:

let cl3 = { print("Hello"); return 42 } as () -> Int

let y1: Int = { print("Hello"); return 42 }()

let y2 = { print("Hello"); return 42 }() as Int

另见Jordan Rose的引用in this mailing list discussion

  

Swift的类型推断目前是面向语句的,因此没有简单的方法来进行[多语句闭包]推理。这至少部分是编译时间问题:Swift的类型系统允许比Haskell或OCaml更多可能的转换,因此解决整个多语句函数的类型不是一个小问题,可能不是一个易处理的问题。

SR-1570 bug report

(两个链接和引用都是从How flatMap API contract transforms Optional input to Non Optional result?)复制的。

答案 1 :(得分:2)

匿名函数的返回类型的推断可能看起来很容易,但事实证明将它构建到编译器中是非常困难的。

因此,通常不允许编写定义和调用初始化程序而不将类型指定为声明的一部分。

但这不是一个大问题。您所要做的就是指定类型!

static var jsonURLNL : URL = ...

(我在脑子里做的是将类型包含在define-and-call初始化器的语法中。所以我总是包含它。所以我从来没有遇到过这个错误信息!)

事后补充:请考虑以下事项:

static var jsonURLNL =  {
    if ProcessInfo.processInfo.environment["CONSUMER"] != nil {
        return "Howdy"
    }
    return URL(string: "professionalURL")!
}()

你看到了问题吗?现在没有可以被推断出来,即使是一个人,因为你在你的回归类型中意外地不一致。但是如果你写: URL,现在编译器知道你所谓的要返回的内容,并且知道"Howdy"是错误的类型。