Languages & Scala 14 Oct 2008 09:15 pm

Implicit Conversions in Scala

In the Ruby programming language, one of its most powerful features is the concept of open classes. In Ruby, classes are never closed: you can always add methods to an existing class to augment its behaviour.

An example of this taken from http://www.ruby-lang.org/en/documentation/ruby-from-other-languages is shown below.

class Fixnum
  def hours
    self * 3600 # number of seconds in an hour
  end
  alias hour hours
end

# 14 hours from 00:00 January 1st
# (aka when you finally wake up )
Time.mktime(2006, 01, 01) + 14.hours # => Sun Jan 01 14:00:00

Scala, while not offering open classes does have a feature that allow you to achieve something similar. Though I will be the first to admit it does make you jump through a few more hoops to achieve the same effect.

Implicit conversion in Scala allows us to extend the behaviour of pre-existing classes without actually changing them.

Imagine we would like to extend the String class with some of the functionality offered by the StringUtils class in the Apach Commons Lang library. Despite this being a java library we can seamlessly use this in Scala due to the tight interoperability with Java.

StringUtils offers a method called abbreviate that will replace a part of a string with ellipsis if the string is longer than specified length.

For example:
StringUtils.abbreviate("abcdefg", 6) = "abc..."
StringUtils.abbreviate("abcdefg", 7) = "abcdefg"
StringUtils.abbreviate("abcdefg", 4) = "a..."

To begin with, lets write a simple utility class in Scala that will delegate to the Apach Commons Lang library that will allow us to abbreviate a string value. Since I have a strong TDD background I would like to start with a simple spec test to illustrate what I am trying to achieve. 

package com.gigantiq.helloworld

import org.specs._

object RichStringSpecification extends Specification {
  "A Rich String" should {
    "abbreviate a long string to a specified length" in {
      new RichString("abcdefg").abbreviate(6) mustMatch "abc..."
    }
  }
}

From this test you can see that I intend on implementing a simple class called RichString which will take a String argument on its constructor and provide an abbreviate method which will delegate to the Apach Commons Lang library.

Implementation RichString gives us a class that looks like this:

package com.gigantiq. implicitconversion

import org.apache.commons.lang.StringUtils

class RichString(original: String) {
def abbreviate(length: Int) = {
    StringUtils.abbreviate(original, length)
  }  
}

Running our spec test will give us a satisfying output.

Specification "RichStringSpecification"
A Rich String should
  + abbreviate a long string to a specified length

  Total for SUT "A Rich String":
  Finished in 0 second, 36 ms
  1 example, 1 assertion, 0 failure, 0 error

This class provides us with the functionality that we are after but not in the seamless way we would ultimately like. In this form every time we wish to abbreviate a String we would need to write code that looked like this:

new RichString("abcdefg").abbreviate(6)

This is not great… actually I might as will stick to Java if this is what i am stuck with.

What I would really like is for my interaction with the abbreviate method to be a little more elegant. In fact, it would be nice if I could seamlessly treat a String as if it was a RichString. This would mean I could write something like this:

“abcdefg”.abbreviate(6)

It gets better than this. If I achieved the above then I could actually get away with syntax that would look more like this:

“abcdefg” abbreviate 6

I would be able to do this since “.” notation before the method call is optional in Scala as is the parenthisis on a method with a single parameter.

Lets rewrite our test with this new expectation.

package com.gigantiq.implicitconversion

import org.specs._

object RichStringSpecification extends Specification {
  "A Rich String" should {
    "abbreviate a long string to a specified length" in {
      “abcdefg" abbreviate 6 mustMatch "abc..."
    }
  }
}

This looks much nicer. How do we do this? The answer is through the use of an Implicit conversion.

We can declare an implicit conversion in Scala with the following syntax:

implicit def stringToRichString(original: String) = new RichString(original)

The “implicit” key word tells the Scala compile that the method “stringToRichString” can be used as an implicit converter.  When the compiler sees a line like “abcdefg" abbreviate 6 it will actually translate the code in to (new RichString(“abcdefg").abbreviate(6)).

Once we have declared the above implicit conversion and run our test we will see everything pass once again.

Specification "RichStringSpecification"
A Rich String should
+ abbreviate a long string to a specified length

Total for SUT "A Rich String":
Finished in 0 second, 36 ms
1 example, 1 assertion, 0 failure, 0 error

Nice :)

Comments are closed.