Incredible implicit array conversion to scala

According to Scaladoc, there is no method in the Array class named map , but there is an implicit function implicit def intArrayOps (xs: Array[Int]): ArrayOps[Int] , defined in scala.Predef . Therefore, you can apply map to Array(1,2,3,4) if you want. But what I am confused about is that the result of the map is of type Array[Int] , not ArrayOps[Int] . Here is my test:

 scala> val array = Array(1,2,3,4) array: Array[Int] = Array(1, 2, 3, 4) scala> array.map(x => x) res18: Array[Int] = Array(1, 2, 3, 4) scala> res18.isInstanceOf[Array[Int]] res19: Boolean = true scala> res18.isInstanceOf[scala.collection.mutable.ArrayOps[Int]] warning: there wre 1 unchecked warnings; re-run with -unchecked for details res20: Boolean = false 
+7
source share
2 answers

It really returns an array, as expected, and, as conveniently, there is no reason why you need ArrayOps, it is only intended to provide additional methods for arrays. The document is erroneous.

In ArrayOps, the procedure is not actually implemented. Like most collection methods, it inherits from TraversableLike. And you see two map methods in the document:

 def map [B] (f: (T) ⇒ B): ArrayOps[B] def map [B, That] (f: (T) ⇒ B)(implicit bf: CanBuildFrom[Array[T], B, That]): That 

Only the second exists (inherited from TraversableLike). It is designed to allow the implementation of the map in only one place (with the possibility of passing), while it always gives the best possible behavior. For example, String is Seq [Char], if you map a function from character to character, you get a string, but if you type from a collection to say Int, the result cannot be a string, and it will just be Seq. This is explained in detail in the article dealing with bit rot with types .

However, this leads to a very complex signature that does not reflect the ease of use of the method and most of the time does very poor documentation (usually you will need to pursue with which CanBuildFrom works in the implicit area). This was discussed in the most famous scala stack overflow issue . In this way, the scaladoc tool has been expanded so that a simpler record appears to fit the intended use. If you look at the source of the GenTraversableLike where the procedure will be introduced, you will see the following in the scaladoc for the map (and similar to one of many methods)

 @usecase def map[B](f: A => B): $Coll[B] 

Subtypes add @define Coll <className> to their document, and the map (among other things) appears with a simplified signature marked [Use case]. In source ArrayOps there is @define Coll ArrayOps , where Array should be.

+11
source

You can use REPL with the -Xprint: typer option to find out what happens. Here is the result of the map method, reformatted for easier reading:

 $ scala -Xprint:typer scala> Array(1,2,3,4).map(x => x) [[syntax trees at end of typer]]// Scala source: <console> // some lines deleted private[this] val res0: Array[Int] = scala.this.Predef.intArrayOps(scala.Array.apply(1, 2, 3, 4)) .map[Int, Array[Int]] (( (x: Int) => x )) (scala.this.Array.canBuildFrom[Int](reflect.this.Manifest.Int)); 

So simplified for package names, here's what happens:

 intArrayOps(Array(1,2,3,4)) // converts to ArrayOps .map[Int, Array[Int]] // calls map with parameter lists below ((x:Int) => x) // pass identity function as fisrt param (Array.canBuildFrom[Int]// pass builder for Array[Int] as second param (Manifest.Int)) // pass class manifest for Int 
  • So there really is a conversion to ArrayOps (first line). It returns ArrayOps[Int] .
  • Then, the method ArrayOps.map[Int, Array[Int]] is called on it. Then, as Didier explained, the original signature for map — not a simplified signature — indicates that the return type will be Array[Int]
+2
source

All Articles