web-dev-qa-db-ja.com

Swiftコンパイラがこのクロージャの型を推測できないのはなぜですか?

だから私は自分のアプリの複数のバージョンを区別するためのコードを書いていた:

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

しかし、私はコンパイラエラーを受け取りました:

複雑なクロージャーの戻り値の型を推測できません。明確にするために明示的な型を追加する

SwiftコンパイラがこれがURLを返すことを認識できないのはなぜですか?この場合はかなり明白だと思います。

この質問の私の目標は、XcodeやSwiftを批判することではなく、コンパイラがSwiftの型を推論する方法についての知識を増やすことです。

13
vrwim

クロージャーの戻り値の型は、クロージャーが単一の式で構成されている場合にのみ自動的に推測されます。次に例を示します。

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

ジョーダンローズの引用もご覧ください このメーリングリストのディスカッションで

Swiftの型推論は現在ステートメント指向であるため、[複数ステートメントクロージャ]推論を行う簡単な方法はありません。これは少なくとも部分的にコンパイル時の問題です。Swiftの型システムでは、HaskellやOCamlなどよりも多くの変換が可能であるため、マルチステートメント関数全体の型を解決することは簡単な問題ではなく、おそらく扱いにくい問題ではありません。

および SR-1570バグレポート

(リンクと引用の両方が flatMap APIコントラクトがオプション入力を非オプション結果に変換する方法)からコピーされます )。

15
Martin R

匿名関数の戻り値の型の推論はyouには簡単に見えるかもしれませんが、コンパイラーに組み込むことは非常に難しいことがわかります。

その結果、一般的に、宣言の一部として型を指定せずに、define-and-call初期化子を書くことは許可されていません。

しかし、それは大きな問題ではありません。タイプを指定するだけです!

static var jsonURLNL : URL = ...

(頭の中で行うことは、型の組み込みを定義および呼び出し初期化子の構文の一部として扱うことです。そのため、常に組み込みます。したがって、このエラーメッセージに遭遇することはありません!)


Afterthought:次の点を考慮してください。

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

あなたは問題を見ますか? nothingは、たとえ人間が誤って戻り値の型に一貫性がないため、たとえ人間であっても推測できます。ただし、: URL、コンパイラはあなたが何であったかを知っていることを知っており、"Howdy"は間違ったタイプです。

3
matt