Scalaのfor comprehension

Scala の for 文には、いろんな機能がある(参考: Scalacheat - Scala Documentation)。for comprehension というやつで、for文を書くと実際には対象オブジェクトのメソッド呼び出しに変換される。

val nestedList = List(List(1,2,3), List(4,5,6))

for {
  list  <- nestedList
  num   <- list
} println(num)

たとえばこのようにコードを書くと、実際には以下の様にforeachを使ったコードが実行されているらしい。

val nestedList = List(List(1,2,3), List(4,5,6))

nestedList.foreach({ list =>
  list.foreach({ num =>
    println(num)
  })
})

for文が値を返すようにyieldをつかうと、flatMapとmapの組み合わせで実行される。

val nestedList = List(List(1,2,3), List(4,5,6))
val nums = for {
  list  <- nestedList
  num   <- list
} yield num

// 以下のように展開される
val nums = nestedList.flatMap({ list =>
  list.map({ num =>
    num
  })
})

以下のコードはコンパイルエラーになる。このエラーがなんで起こるのかすぐにわからなかった。

val option = Option(List(1,2,3))

val nums = for {
  list <- option
  num  <- list
} yield num

// [error]  found   : List[Int]
// [error]  required: Option[?]
// [error]     num  <- list
// [error]          ^

このコードは以下のflatMap/mapの組み合わせに変換される。

val nums = option.flatMap({ list =>
  list.map({ num =>
    num
  })
})

Option型のflatMapは flatMap[B](f: (A) ⇒ Option[B]): Option[B] という型になっていて、flatMapに渡した無名関数はOption型の値を返さないといけない。一方、List型のmapはList[Int]を返しているために、ここではエラーになる。

概念的にはOptionの中身を変換するという操作のはずだから、いきなりListに変わったりするのはおかしいという感じな気がする。

ちなみに、yieldがない場合にはforeachに変換されるので上記のエラーは発生しない