Scala compiler does not behave as expected using implicit val value in null

I came across this very peculiar behavior that made me hold back for a while. I updated it in the following snippet below, the code is removed from the Scala wrapper.

scala> def a = { | implicit val u = null | val x: Int = List(1,2,3).map(_.toString) | } a: Unit 

There is no error in the above code, although I know that the val x type is List [String]. I can change the type of x to Int, Long, etc., And it will continue to compile the fine.

However, when I add an explicit type for the implicit val u, as in the example below, the compiler behaves as expected and produces an error.

 scala> def a = { | implicit val u: Any = null | val x: Int = List(1,2,3).map(_.toString) | } <console>:10: error: type mismatch; found : List[String] required: Int 

Has anyone else experienced this or have any idea why this is happening?

+4
source share
2 answers

Due to the way collections are created in scala, null receives map as an implicit parameter, which has the following extended signature:

 def map[B, That](f: A => B)(implicit bf: CanBuildFrom[List[A], B, That]): That 

In this particular example, the implicit parameter should be CanBuildFrom[List[Int], String, Int] , and unfortunately null is a lower type, so it satisfies this requirement.

Check out this QA for more details: How CanBuildFrom knows if a type can build from another?

You can reproduce it without implication as follows:

 @ def a = { val x: Int = List(1,2,3).map(_.toString)(null) } defined function a 

Then, when you actually run this function, it tries to use the builder, but this value is null, so ... BOOM!

 @ a java.lang.NullPointerException scala.collection.TraversableLike$class.builder$1(TraversableLike.scala:240) scala.collection.TraversableLike$class.map(TraversableLike.scala:244) scala.collection.immutable.List.map(List.scala:285) cmd3$.a(Main.scala:52) cmd4$$anonfun$1.apply$mcV$sp(Main.scala:52) cmd4$.<init>(Main.scala:53) cmd4$.<clinit>(Main.scala:-1) 

you can see on the stack that everything breaks as soon as the implementation of the map on TraversableLike tries to access the builder , which turns out to be null .

+8
source

When you look at the map signature, you see that there is an implicit argument:

  def map[B, That](f: A => B)(implicit bf: CanBuildFrom[List[A], B, That]): That 

When the compiler sees an implicit value inside the map call area, it seems to be assumed that it should be CanBuildFrom[List[Int], String, Int] . Therefore, it expands to:

 def a = { implicit val u: CanBuildFrom[List[Int], String, Int] = null val x: Int = List(1, 2, 3).map(_.toString)(u) } 

If you try to use it, you can expect a NullPointerException .

Now that you give u an explicit type, the compiler can no longer find or output CanBuildFrom , which will give the result type Int .

+4
source

All Articles