I encountered the following weird issue when having option in nested collections:
val works: Array[Option[Int]] = Array(1)
.map { t => Some(t)}
val fails: Array[Array[Option[Int]]] = Array(Array(1))
.map { ts => ts.map { Some(_)} }
// error: type mismatch; found : Array[Array[Some[Int]]] required: Array[Array[Option[Int]]]
val worksButUgly: Array[Array[Option[Int]]] = Array(Array(1))
.map { ts => ts.map { case t => (Some(t).asInstanceOf[Option[Int]])}}
I imagine it may be a problem with some type erasure along the way but is it the expected behaviour in Scala? Does anyone knows what happens exactly?
Arrays in Scala are invariant. This prevents some problems that arrays have in e.g. Java, where you can create an array of something, proclaim it to be an array of superclass of something, and then put another subclass in. For example, saying that an array of apples is an array of fruit and then putting bananas in. The worst part about this is that it fails at runtime, not at compile time.
For this reason Scala decided that arrays should be invariant. This means that Array[Apple]
is not a subclass of Array[Fruit]
. (Note that unlike arrays, immutable collections are most often covariant, e.g. List
, because immutability prevents us from putting any bananas inside later on)
So yes. Some
is a subclass of Option
, but Array[Some]
is not a subclass of Array[Option]
. These will work:
val foo1: Array[Array[Option[Int]]] = Array(Array(1))
.map { ts => ts.map { Option(_)} }
val foo2: Array[List[Option[Int]]] = Array(List(1))
.map { ts => ts.map { Some(_)} }