Swift – Why can’t the Swift compiler infer this closure’s type

swifttype-inference

So I was writing code to differentiate multiple versions of my app:

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

But I got a compiler error:

Unable to infer complex closure return type; add explicit type to disambiguate

Why can't the Swift compiler know that this will return a URL? I think it is fairly obvious in this case.

My goal with this question is not to give critique on Xcode or Swift, it is to increase my knowledge of how the compiler infers types in Swift.

Best Answer

The return type of a closure is only inferred automatically if the closure consists of a single expression, for example:

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

or if the type can be inferred from the calling context:

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

or

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

Simplified examples: This single-expression closure compiles:

let cl1 = { return 42 }

but this multi-expression closure doesn't:

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

The following lines compile because the type is inferred from the context:

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

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

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

See also the quote from Jordan Rose in this mailing list discussion:

Swift's type inference is currently statement-oriented, so there's no easy way to do [multiple-statement closure] inference. This is at least partly a compilation-time concern: Swift's type system allows many more possible conversions than, say, Haskell or OCaml, so solving the types for an entire multi-statement function is not a trivial problem, possibly not a tractable problem.

and the SR-1570 bug report.

(Both links and the quote are copied from How flatMap API contract transforms Optional input to Non Optional result?).

Related Topic