読者です 読者をやめる 読者になる 読者になる

Swiftで遊ぼう! on Hatena

あしたさぬきblogでやってた初心者オヤジのiOSプログラミング奮闘記がHatenaに来ました

Swiftで遊ぼう! - 741 - 推測機能の制限

昨日はflatMapの勉強になってませんでした。

flatMapのリファレンスを調べると、タイプの違いによって宣言法が2つ用意されています。

  1. flatMap(_:) - Sequence | Apple Developer Documentation
  2. flatMap(_:) - Sequence | Apple Developer Documentation

Swiftには強力な推測機能が備わっているけど、「コード1行にしか影響しない」という制限があるというブログ記事*1をみました。

まず次のコードを考えます。

let a = [[1,2],[3],[4,5,6]]
var b: [Int]
b = a.flatMap { elem in
    return elem }

ここでクロージャーには推測機能が働いているので、クロージャーの型は「([Int]) -> [Int]」と考え、「elem」は「[Int]」と考えるのが自然です。

じゃあ次のコードはどうなるんでしょう。

b = a.flatMap { elem2 in
    print(elem2)
    return elem2
}

途中で「print(elem2)」が加わっただけで、最初のコードと何ら変わりがないのですが、このコードではエラーが出ます。

cannot convert return expression of type '[Int]' to return type 'Int?'

あれ?どうして戻り値が「Int?」になっているんだろう?どうして推測機能がうまく動かないのだろう。

swift-devメーリングリストでSlava Pestov氏が次のような投稿をしています。

概念的に、クロージャや複数行で構成する関数に備わっているグローバル型推測機能をサポートするための型チェックアルゴリズムに大きな変化は加えられず、考え方にもよるが、プログラミング言語の進むべき方向性ではないと感じています。グローバル型推測機能は、今後型チェック絡みでパフォーマンス悪化の問題に直面し、それと同様にエラー診断機能の構築を困難にさせます。

ということで推測機能はシングル行にしか効果を発揮しないうえに、上記で示したようにflatMapには2つの定義があり、推測する時に間違いが生じるようです。

ということで、これを避けるために次の様に明示すべきなんですね。

b = a.flatMap { (elem2: [Int]) -> [Int] in
  print(elem2)
  return elem2
}

まあ何にしろ推測機能も万能じゃないのでエラーが出たときは考え込みそうです。

今日はこれだけ。

*1:どこのブログが知りたいですが?