Implicit Conversions: Scala's Type Safe Answer To Ruby's Open Class

April 17, 2009

Let's say that you are writing an application that squares things often, so you would like to be able to do this:

>> 4.squared
=> 16

In Ruby, that's easy-peasy:

class Integer
  def squared
    self * self
  end
end

Wham. Done. Open it right up, shove your method in there and you are bending the language to your will. But what if you couldn't modify existing classes? Maybe you would do this:

class IntegerWrapper
  def initialize(value)
    @value = value
  end
  def squared
    @value * @value
  end
end

Cool. Now you haven't modified Integer, but you get the same effect. So you just use it like this:

>> IntegerWrapper.new(4).squared
=> 16

WOAH! WTF? That is a lot of extra syntax. Having all to call wrapper classes like this really makes it too verbose. So now we shift gears into Scala mode:

scala> class IntegerWrapper(val value : Int) { def squared = value * value }
defined class IntegerWrapper

scala> new IntegerWrapper(4).squared
res0: Int = 16

As you can see on the first line, we define the same IntegerWrapper class. Then we use it just the same way we do in Ruby. But now we throw in an implicit conversion to make the syntactic magic happen:

scala> implicit def wrapInt(i:Int) = new IntegerWrapper(i)
wrapInt: (Int)IntegerWrapper

scala> 4.squared
res1: Int = 16

The implicit def defines an implicit conversion from an Int to an IntegerWrapper. This is our way to tell scala that if we try to call squared on an Int, use this function to convert the Int to an IntegerWrapper for me. Now as you can see, we can call squared on what appears to be a Int, and get the same syntactic advantage of adding methods to existing classes, without really modifying existing classes.

Posted in Technology | Tags Scala, Ruby

Comments

1.

Great summary of what we reviewed on Monday. Much easier to understand the value.

# Posted By Rob Carlson on Friday, April 17 2009 at 9:46 AM

2.

Great explanation, Paul. The neat part is the lookup rules the parser uses to find implicit conversions. For example, you can define your implicits elsewhere, and then import them.

//somewhere else
object MyWrappers { implicit def Int2IntegerWrapper(i:Int) = new IntegerWrapper(i) }

//in this scope
import MyWrappers._

4.squared //works fine

# Posted By Jonathan Julian on Friday, April 17 2009 at 1:43 PM

3.

Beat me to it Paul! I suppose it's my fault for waiting all week to sit down and write this down.

The example you used is a good one.

-John

# Posted By John Trupiano on Friday, April 17 2009 at 4:54 PM

Comments Disabled