4
votes

I was recently surprised when invocations of Scala's implicitly was returning null during runtime. I had thought that was unlikely, given that the code shouldn't compile if no implicit instance was available in implicit scope. When is implicitly allowed to return null? Is this a compiler limitation, or is this expected behavior?

Here's some context, if it helps. I'm using shapeless to derive typeclass instances for persisting arbitrary nested case classes. I thought it would be helpful to use implicitly in the nested case classes to check if a typeclass instance could be derived, as it can be unclear where to start looking if the nested case class is large.

So, for example, if I was trying to persist:

case class Foo(bar: Bar, baz: Baz)

and the compiler couldn't derive an instance for my formatter MyFormatter[Foo], I started doing something like the following:

case class Bar(i: Int, q: Qux)
object Bar {
  implicit val formatter = implicitly[MyFormatter[Bar]]
}

expecting the compiler to tell me that it couldn't find an implicit instance of MyFormatter[Bar].

Instead, this was a terrible idea, and my code compiled (when it shouldn't have, as no typeclass instance for Qux could be derived) and Bar.formatter was null at runtime.

1
There's something wrong with the code relevant to MyFormatter. Something is making that implicit, but it's impossible to say what without seeing it. The Scala compiler won't magically create one that is null on its own.Michael Zajac
I hear what you're saying: but short of a definition like implicit val oyVey: MyFormatter[Bar] = null (which I promise I didn't do) what would make that possible?Josh Marcus
I don't know without seeing the code. implicitly grants no magic. You could try println(scala.reflect.runtime.universe.reify(implicitly[MyFormatter[Foo]])) to see where it is being resolved from.Michael Zajac

1 Answers

8
votes

Your implicit definition is recursive.

scala> class C ; object C { implicit val cs: List[C] = implicitly[List[C]] }
defined class C
defined object C

scala> C.cs
res0: List[C] = null

Not only is cs in scope, but object C is in implicit scope for List[C].

Also, it's preferred to specify the type of implicits; sometimes it is necessary for inference to work; and someday it will be required.