Search code examples
arraysscalaoption-typefor-comprehension

For comprehension over Option array


I am getting compilation error:

Error:(64, 9) type mismatch;
 found   : Array[(String, String)]
 required: Option[?]
      y <- x
        ^

in a fragment:

val z = Some(Array("a"->"b", "c" -> "d"))
val l = for(
  x <- z;
  y <- x
) yield y

Why generator over Array does not produce items of the array? And where from requirement to have Option is coming from?

To be more ridiculous, if I replace "yield" with println(y) then it does compile.

Scala version: 2.10.6


Solution

  • This is because of the way for expressions are translated into map, flatmap and foreach expressions. Let's first simplify your example:

    val someArray: Some[Array[Int]] = Some(Array(1, 2, 3))
    val l = for {
      array: Array[Int] <- someArray
      number: Int <- array
    } yield number
    

    In accordance with the relevant part of the Scala language specification, this first gets translated into

    someArray.flatMap {case array => for (number <- array) yield number}
    

    which in turn gets translated into

    someArray.flatMap {case array => array.map{case number => number}}
    

    The problem is that someArray.flatMap expects a function from Array[Int] to Option[Array[Int]], whereas we've provided a function from Array[Int] to Array[Int].

    The reason the compilation error goes away if yield number is replaced by println(number) is that for loops are translated differently from for comprehensions: it will now be translated as someArray.foreach{case array => array.foreach {case item => println(item)}}, which doesn't have the same typing issues.

    A possible solution is to begin by converting the Option to the kind of collection you want to end up with, so that its flatMap method will have the right signature:

    val l = for {
      array: Array[Int] <- someArray.toArray
      number: Int <- array
    } yield number