June 11, 2011

Further Scala: Implicit conversions

My attempts to learn scala continue…

Implicit conversions are a cool, if slightly unsettling (from a java programmers POV) scala feature. If I have an instance of one class, and I try and call a method on it which is defined in a different class, then if there’s an “implicit” method in scope which will convert between the two, scala will silently use it.
e.g.

scala> var x = 12
x: Int = 12

scala> x.substring(1,2)
<console>:9: error: value substring is not a member of Int
       x.substring(1,2)

scala> implicit def foo(i:Int):String={i.toString}
foo: (i: Int)String

scala> 12.substring(1,2)
res10: java.lang.String = 2

WITCHCRAFT! BURN THE COMPILER!

This lends itself to a very very useful trick; the ability to enhance classes with additional methods. Say you had a java Map class, and you wanted the ability to merge it with another Map according to some sort of merge function on the values. You’d probably do it like this:

class MergeableMap implements Map{

public MergeableMap(Map delegate){
 this.delegate = delegate
}

public Map merge(Map otherMap, ValueMergingFunction mergeFunction){
 ....
}

... delegate implementations of all Map methods here...
}

Trouble is, (a) writing all the delegate methods is tedious, and(b) every time you want to use it, you’ve got to do

MergeableMap m = new MergeableMap(myMapVariable)
m.merge(myOtherMap,...)

Implicits in scala make this a lot easier:

class MergeableMap[A, B](self: Map[A, B]) {
  def merge(m1, merger): Map[A, B] = {
... implementation here...
  }
}

implicit def map2mergeableMap[A,B](m:Map[A,B]):MergeableMap[A,B] = new MergeableMap(m)

myMap.merge(myOtherMap, myMergeFunction)
myMap.get(...)

there’s no need to implement the other delegate methods, since we can just call them on the original Map class – but when we call merge() compiler-based voodoo works out that we want a mergeable map, and swaps it in for us. Magical.


- 2 comments by 1 or more people Not publicly viewable

  1. Colin Yates

    This is so much nicer than Groovy’s dynamic methods. I wonder if it is faster at run-time….

    12 Jun 2011, 10:53

  2. Chris May

    I tried to test that, but found an interesting result…

    scala>  implicit def foo(i:Int):String={i.toString}                         
    foo: (Int)String
    scala> (1 to 100000000).foldLeft(0)((x:Int,y:Int)=>(x.substring(0,1).length))
    res0: Int = 1

    - 6 seconds

    groovy:000> Integer.metaClass.substring= {f,t ->intValue().toString().substring(f,t)}
    groovy:000> (1..100000000).inject(0){t,i -> i.substring(0,1).length()}

    -1 min 26 seconds

    I don’t suppose that overhead is all down to metaclass fiddling. Someone with time to kill could make a proper benchmark that pre-compiles code and runs a large number of iterations of method calls with and without enriched classes in both langugages, and look at the difference. If that test is at all representative, though, I don’t think that the time taken to inject a new method is going to be the critical component in a groovy vs. scala performance shootout.

    13 Jun 2011, 15:54


Add a comment

You are not allowed to comment on this entry as it has restricted commenting permissions.

Most recent entries

Loading…

Search this blog

on twitter...


    Tags

    Not signed in
    Sign in

    Powered by BlogBuilder
    © MMXIX