Thursday, December 29, 2011

Scala's implicit need to be handled with care

Quick post when using Scala's implicits. These constructs are really useful in the manner that they allow the user to create rather complex expressions in type design & bridging frameworks in the Java world. However, i did find some oddity when i was playing with it and found something interesting, to me.

Let's begin with a simple example.

scala> object holder {
     | trait Foo {
     | implicit val x = new Foo { override def toString = "trait foo" } }
     | object Foo {
     | implicit val y = new Foo { override def toString = "object foo" } }
     | }
defined module holder


scala> import holder.Foo
import holder.Foo


scala> def method(implicit foo: Foo) = println(foo)
method: (implicit foo: holder.Foo)Unit


scala> method
java.lang.StackOverflowError
at holder$Foo$$anon$1.(:18)
at holder$Foo$class.$init$(:18)
at holder$Foo$$anon$1.(:18)
at holder$Foo$class.$init$(:18)
at holder$Foo$$anon$1.(:18)
at holder$Foo$class.$init$(:18)
at holder$Foo$$anon$1.(:18)
at holder$Foo$class.$init$(:18)
at holder$Foo$$anon$1.(:18)
at holder$Foo$class.$init$(:18)

The interesting thing is that the Scala runtime can't seem to make up its mind whether to invoke the "x" or the "y" when its being looked up in the call to "method".

Another example would be related to Scala's package objects. To see how the problem can manifest itself, here's an example with a file named package.scala in Scala's convention and here are its contents:

package object foo {
  implicit def foo = new Foo
  implicit def foo2 = new Foo
}
package foo {
  class Foo {
    override def toString = "FOO!"
  }
}

when you run the compilation via scalac, it generates a directory foo and dumps the class files there. Nothing to it. Now run scala and use the implicitly function and you'll see what i mean.

Welcome to Scala version 2.9.1.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_29).
Type in expressions to have them evaluated.
Type :help for more information.


scala> implicitly[foo.Foo]
:8: error: ambiguous implicit values:
 both method foo in package foo of type => foo.Foo
 and method foo2 in package foo of type => foo.Foo
 match expected type foo.Foo
              implicitly[foo.Foo]

This isn't Scala's fault, really; because the compiler can't possibly know what you plan to do with it but it certainly is something you should be aware of and i do think this is a good thing (rather than having the compiler just forget about it)

The idea is to really limit the scope in which the implicits exist so as to reduce the amount of pain you have to go through when debugging

0 comments: