Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the scaladoc say HashMap.toArray returns Array[A] instead of Array[(A,B)]?

Tags:

scala

scaladoc

I was looking at the definition of toArray for hashmaps :

http://www.scala-lang.org/api/current/index.html#scala.collection.immutable.HashMap

It has

toArray: Array[A]
def toArray[B >: (A, B)](implicit arg0: ClassTag[B]): Array[B]

I don't quite understand this - the first bit says you get an Array[A], but the second part says you get Array[B]? Neither of these are what I expect - Array[(A,B)]

When I check it myself :

scala> val x = scala.collection.mutable.HashMap[String, Int]()
x: scala.collection.mutable.HashMap[String,Int] = Map()

scala> x.put("8", 7)
res0: Option[Int] = None

scala> x foreach println
(8,7)

scala> x.toArray
res2: Array[(String, Int)] = Array((8,7))

why isn't it like toList?

toList: scala.List[(A, B)]
like image 648
Austin Avatar asked Jun 24 '13 14:06

Austin


2 Answers

The scaladoc has all kinds of subtle bugs. The problem here is that you are seeing the "simplified" version of the method signature (meant as a way to convey the essential part of the signature and hide things such as CanBuildFrom in map/flatMap methods, which are really an implementation detail). The simplification got a little awry here, and does not seem to make much sense. If you click on the "full signature" link, you'll see that the real signature looks like:

def toArray[B >: (A, B)](implicit arg0: ClassTag[B]): Array[B]

In fact this is still wrong, as we certainly cannot have a type B where B >: (A, B). It should be more like :

def toArray[C >: (A, B)](implicit arg0: ClassTag[C]): Array[C]

The problem is that there are actually two Bs: the first one comes from the HashMap class declaration itself (HashMap[A, +B]) while the other one comes from the methods toArray defined in its base class TraversableOnce (def toArray[B >: A](implicit arg0: ClassTag[B]): Array[B]). It just happens that the scaladoc generator failed to dedup the two instances of B

like image 80
Régis Jean-Gilles Avatar answered Oct 05 '22 22:10

Régis Jean-Gilles


The API you see in the the Scaladoc of toArray:

def toArray[B >: (A, B)](implicit arg0: ClassTag[B]): Array[B]

Is equivalent to:

def toArray[C >: (A, B)](implicit arg0: ClassTag[C]): Array[C]

The choice of the type variable B is indeed unfortunate (and maybe even a Scaladoc bug, I'm not sure if you are allowed to write that).

It basically means you'll get an array of the most specific supertype of (A,B) for which a ClassTag is available. The ClassTag is required in order to create the Array.

This basically means that if at compile-time, the-run time type of the Map you are converting is fully known, you will get a Array[(A,B)]. However, if you have up-casted your Map somewhere, the run-time type of the resulting Array will depend on the up-casted type, and not on the runtime type. This is different behavior than toList and due to the JVMs restrictions on how native arrays can be created.

like image 31
gzm0 Avatar answered Oct 05 '22 22:10

gzm0