Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Odd typing bug in Scala

Tags:

types

scala

Take a look at this:

scala> class Container(val rows: Iterable[Iterable[Option[Any]]]) {} 
defined class Container

scala> val row1 = Array(Some("test"),Some(1234))
row1: Array[Some[Any]] = Array(Some(test), Some(1234))

scala> val row2 = Array(Some("test2"), Some(12345))
row2: Array[Some[Any]] = Array(Some(test2), Some(12345))

scala> val listtest = List(row1, row2)
listtest: List[Array[Some[Any]]] = List(Array(Some(test), Some(1234)), Array(Some(test2), Some(12345)))

scala> val test = new Container(listtest)
<console>:11: error: type mismatch;
 found   : List[Array[Some[Any]]]
 required: Iterable[Iterable[Option[Any]]]
       val test = new Container(listtest)
                                ^

scala> val test = new Container(List(row1,row2))
test: Container= Container@600a08

How come defining the Container the second way works, but the first one doesn't? Aren't the types the same?

like image 325
Henry Henrinson Avatar asked Sep 02 '11 09:09

Henry Henrinson


1 Answers

This is no bug. Array is not covariant. B being a subtype of B does not make Array[B] a subtype of Array[A]. This is contrary to java, where B[] is a subtype of A[], which is unsound:

A[] b = new B[1];
b[0] = new (A);
-> ArrayStoreException

So your Array[Some[Any]] is not an Array[Option[Any]]. You must make sure you have an Array[Option], which you may do with

val row2 = Array[Option[Any]](Some(test2), Some(12345))

You can also use a type abscription on one of the item:

val row2 = Array(Some(test2): Option[String], Some(12345))

Or if you know your values are non-null,

val row2 = Array(Option(test2), Option(12345))

(It's enough to do that on one of the value)

List, on the other hand is covariant, which is why it works.

It is actually unfortunate that the more precise type Some is inferred, it is quite uncommon that you want the type of something to be known as Some (or None) rather than Option.

Edit Sorry, looks like I completely missed the point, about why there are different. Array is not covariant, but Iterable is. So it would seems that Array[B] while not being an Array[A], should be an Iterable[A], then everything should work. It would be so if Array was a subtype of Iterable. It is not, it comes with the JVM, cannot be made to extend Iterable. What there is is an implicit conversion to WrappedArray, which is an Iterable.

When you write val l = List(row1, row2), it has no reason to apply this conversion. It types list as precisely as it can. Then the fact that List is covariant (a List[B] is a List[A] if B is an A) will not kick in when we have not B is an A, but B has an implicit conversion to A.

On the other hand, when you write val l: List[Iterable[A]] = List(x,y) then the List(...) function expects Iterable[A] arguments, and at this point it looks for implicit conversions.

Still not a bug, but trickier than I thought. Maybe you could do a

class Container[T <% Iterable[Option[Any]]](val rows: Iterable[T])
like image 56
Didier Dupont Avatar answered Oct 11 '22 12:10

Didier Dupont