Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala compiler doesn't behave as expected with use of an implicit val set to null

Tags:

scala

I encountered this very peculiar behaviour that had me stumped for quite awhile. I've re-created it in a simple snippet below, code is lifted from Scala wrapper.

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

In the code above, there is no error thrown even though I know the type of val x to be List[String]. I can change the type of x to Int, Long, etc. and it will continue to compile fine.

However when I add explicitly state a type for the implicit val u, like in the example below, the compiler behaves as expected and throws 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 has any insight into why this is happening?

like image 706
trashbear Avatar asked Dec 10 '22 21:12

trashbear


1 Answers

Due to how collections are designed in scala, null gets picked up as the implicit parameter of map, which has the following extended signature:

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

In this specific example, the implicit parameter must be a CanBuildFrom[List[Int], String, Int], and unfortunately null is a bottom type so it satisfies such requirement.

Check this QA for more details: How does CanBuildFrom know whether a type can build from another?

You can reproduce it without the implicit like this:

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

Then, when you actually run the function, it tries to apply the builder but it's 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 in the stacktrace that everything breaks as soon as the implementation of map on TraversableLike tries to access builder, which happens to be null.

like image 93
Gabriele Petronella Avatar answered Feb 01 '23 23:02

Gabriele Petronella