Scala Arrays vs Vectors

2019-02-12 22:40发布

问题:

Scala newb... I'm confused

object myApp extends App {
  println("Echo" + (args mkString " "))
}

"args" is type Array[String], but in the scaladoc, Array has no such method. mkString is a method for Vector, but I don't see any inheritance link between the two. So why can we use the mkString method on args?

回答1:

I'm not a scala expert (far from it!) but I think the answer is implicit conversions (see scala.Predef) and WrappedArray.scala.

In particular, Predef has the following implicit conversion:

implicit def genericWrapArray [T] (xs: Array[T]): WrappedArray[T] 

And WrappedArray has a mkString method. When scala can't find a mkString method on Array, it looks for an implicit conversion to a type that does.

http://www.scala-lang.org/api/current/scala/Predef$.html

http://www.scala-lang.org/api/current/scala/collection/mutable/WrappedArray.html



回答2:

Expanding on Kevin's answer and explaining why it's not possibly for scaladoc to tell you what implicit conversion exists: implicit conversions only come into play when your code would not compile otherwise.

You can see it as an error recovery mechanism for type errors that is activated during compilation. In this case, Array[String] does not have a mkString method. This code would not compile, because that method does not exists on Array[T]. But before giving up the compiler will look for an implicit conversion in scope.

It happens that Predef brings a number of implicit conversions in scope and one that will apply here.

Finding out which implicit conversion applies can be done by compiling with the -Xprint:typer flag. In this case it would print:

$ scalac -d classes -Xprint:typer A.scala
[[syntax trees at end of typer]]// Scala source: A.scala
package <empty> {
  final object myApp extends java.lang.Object with App with ScalaObject {
    def this(): object myApp = {
      myApp.super.this();
      ()
    };
    scala.this.Predef.println("Echo ".+(scala.this.Predef.refArrayOps[String](myApp.this.args).mkString(" ")))
  }
}

So you can see that Predef.refArrayOps is in fact the implicit conversion used. It converts your array into a ArrayOps[String] which does have a mkString method.

So with that in mind you can see why scaladoc for Array cannot tell you what implicit conversion could apply. It could be anything. It is in fact wholly based on the fact that there is no such method. Only the compiler will know what implicit it found based on the code.

You can even define your own implicit conversion:

object myApp extends App {
  implicit def myImplicit(arr:Array[String]) = new {
    def mkString(s:String) = arr.length + s
  }
  println("Echo " + (args mkString(" ")))
}

Which would have the following effect:

$ scala -cp classes myApp a b c
Echo 3

Obviously scaladoc won't be able to show that. Note that the Eclipse Scala plug in can bring you to the implementation of mkString by pressing F3 (you'll end up in TraversableOnce).



回答3:

But Scaladoc could at least say that Predef (which is special because it's always in scope) has an implicit conversion from Array. That would be useful.