Scala String Interpolation - It Happened!

Scala 2.10 introduces String Interpolation

I always wanted Scala to have something like Ruby string interpolation. It’s a pretty small feature and somebody would definitely call it unnecessary syntactic sugar (e.g. Java has no string interpolation at all) but it always felt just right speaking about Scala. Starting in Scala 2.10 there is a new mechanism for it called String Interpolation (who would have thought!). The documentation can be found here with corresponding SIP here. It’s quite small overview so I would recommend to read it through. Although the documentation is clear (it’s not so much to be covered actually) I would like to highlight a few points.

String Interpolation is safe

There are three string interpolation methods out of the box: s, f and raw. Let’s look at s String Interpolator in action with variables:

1
2
3
scala> val x = 1
scala> s"x is $x"
res1: String = x is 1

and with expressions:

1
2
scala> s"expr 1+1 equals to ${1 + 1}"
res2: String = expr 1+1 equals to 2

So it works as expected (at least for a Ruby developer :)). Let’s try to interpolate a variable that doesn’t exist:

1
2
3
4
scala> s"name doesn't exist $name"
<console>:8: error: not found: value name
              s"name doesn't exist $name"
                                    ^

It just doesn’t compile at all! Very nice of the compiler, isn’t it? If you have read the documentation it’s not difficult to understand why it’s safe. If not we’ll touch this in the next section anyway.

String Interpolation is extensible

If you’re still asking yourself what is this s before string literal the answer is that processed string literal is a code transformation which compiler transforms into a method call s on an instance of StringContext. In other words expression like

1
s"x is $x"

is rewritten by compiler to

1
StringContext("x is ", "").s(x)

First of all it explains why using string interpolation is safe (see previous section for example). Using nonexistent variable as a parameter for method call leads to not found: value [nonexistent variable here] error. In the second place it allows us to define our own string interpolators and reuse existing ones.

To see how it’s easy let’s create our own string interpolator which will work as s interpolator with added some debug info to the resulting string:

Creation and usage of simple Log Interpolator
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.util.Date
import java.text.SimpleDateFormat

object Interpolation {
  implicit class LogInterpolator(val sc: StringContext) extends AnyVal {
    def log(args: Any*): String = {
      val timeFormat = new SimpleDateFormat("HH:mm:ss")
      s"[DEBUG ${timeFormat.format(new Date)}] ${sc.s(args:_*)}"
    }
  }

  val logString = "one plus one is"
  def demo = log"$logString ${1+1}"
}

In the code above implicit classes and extending AnyVal (so called Value Classes) are also new features in Scala 2.10 which we’ll talk about later. Since any interpolator is in fact a method of StringContext class we can easily use them in our own ones (in the example we use s method forming the resulting string to not bother with implementing it in our new interpolator). The string interpolation

1
log"$logString ${1+1}"

will be rewritten by compiler to

1
new LogInterpolator(new StringContext("", " ", "")).log(logString, 2)

which is a nice combination of new Scala 2.10 features itself.

This new technique is useful writing more readable code, safe and allows to extend and combine existing functionality. The only limitation that it’s not working within pattern matching statements but it’s going to be implemented in Scala 2.11 release. I would call it String Interpolation with Batteries Included :)