Scala 2 implicit conversions

Scala is a statically typed language, meaning that Scala compiler will know types of every variable we use in our programs at compile time. To play well with compiler we will have to specify each variable’s type.

This can feel as a tedious work and less flexible than dynamic languages such as PHP, Ruby or Python. But Scala and its compiler have features that ease things and make you enjoy the best of 2 worlds.

First one is the well-known type inference. I will not cover it on this post but this is a great feature of Scala compiler that can often infer type of expression/variable. You almost don’t have to specify type of variable when you’re declaring it.

Second one which appears to be less known (as it’s more hidden I think) is concept of implicit conversions.

Take following example:

scala> val numbers: Range = 1 until 1000 // same as 1.until(1000)
numbers: Range = Range 1 until 1000

What just happened her? 1 is an Int but until is not defined in Int class. So how did Scala succeed to understand that?

When Scala compiler encountered method until applied on 1 being an Int, it did an implicit conversion from an Int to a RichInt because until applies on a RichInt type.

This is one of Scala compiler features, it can detect and convert given arguments types to match wanted expression.

In the example we wanted to apply method until on a number, compiler searched an implicit conversion that could take this number and return something where until is defined, in this case a RichInt.

Debug implicits conversions

To confirm previous example behavior, we can see what Scala compiler generates when it parses our code to then transform it into JVM byte code. We can plug our debug at different steps of this process phases. Step that interests us is typer phase, as it’s during this phase that implicit conversions are applied.

Let’s put our initial example inside a valid Scala file named Test.scala:

// Test.scala
object Test {
  def main(args: Array[String]): Unit = {
    val numbers: Range = 1 until 10
  }
}

Now let’s compile it with debug enabled on typer phase with this command:

$ scalac -Xprint:typer Test.scala

Compiler displays what is state of generated code at end of the phase:

1
2
3
4
5
6
7
8
9
10
11
12
13
[[syntax trees at end of                     typer]] // Test.scala
package <empty> {
  object Test extends scala.AnyRef {
    def <init>(): Test.type = {
      Test.super.<init>();
      ()
    };
    def main(args: Array[String]): Unit = {
      val numbers: scala.collection.immutable.Range = scala.Predef.intWrapper(1).until(10);
      ()
    }
  }
}

Alongside boilerplate code interesting part is at line 9, when we can see that our initial 1 is now a scala.Predef.intWrapper(1).

If we look at source code of Predef.scala in Scala API, we can find this method defined as: implicit def intWrapper(x: Int) = new runtime.RichInt(x).

Our assumption was true. Compiler understood our code and applied conversion.

Note: more informations about compiler phases here: https://typelevel.org/scala/docs/phases.html

Import implicits conversions

If we look again at Predef.scala file. We can see that Scala has many implicit conversions predefined and enabled by default.

We can also explicitly import conversions defined in rest of Scala API (but not enabled by default), like ones defined in Duration utility class, as below:

scala> import scala.concurrent.duration.DurationInt
import scala.concurrent.duration.DurationInt

scala> val time = 180 seconds
time: scala.concurrent.duration.FiniteDuration = 180 seconds

scala> time.toMinutes
res8: Long = 3

Create implicits conversions

Creating a new implicit conversion is as simple as defining a method with implicit keyword and having it available in scope where we want to apply conversion:

scala> implicit def intToString(x: Int) = x.toString
intToString: (x: Int)String

scala> val a: String = 11
a: String = 11

This example was straightforward, a more interesting and useful example is to create an implicit class to provide new methods on a Type:

object MathImprovements {
  implicit class AlmostEquals(val d: Double) {
    def ~=(d2: Double): Boolean = (d - d2).abs <= 0.0001
  }
}

object Test {
  import MathImprovements.AlmostEquals

  def main(args: Array[String]): Unit = {
    print(1.0002 ~= 1.0001) // true
  }
}

Here implicit class AlmostEquals provides a method ~= on a Double type to see if it has same first 3 decimals with another Double. Definition of AlmostEquals is same as a normal class definition plus implicit keyword.

Note: an implicit class must live either in another class, object or trait, but not at top level. This is a restriction in order to make their usages explicit by importing them where needed.

Conclusion

Implicit conversions (and implicits in general) is one of main feature of Scala. They are mainly used to add new functionalities on Types which are closed by design. I would recommend to use them with precaution as they can make code harder to debug without a good tooling like an IDE. But they are a good way to reduce boilerplate code and increase readability.