Scala String Interpolation

Introduction

String Interpolation refers to substitution of defined variables or expressions in a given String with respected values. String Interpolation allows users to embed variable references directly in processed string literals. Here’s an example:

val pi = 3.14  
println(s"value of pi = $pi") // value of pi = 3.14

And, here’s an example which does not uses any string interpolator.

val pi = 3.14  
println("value of pi = "+pi) // value of pi = 3.14

Starting in Scala 2.10.0, Scala offers a new mechanism to create strings from your data: String Interpolation.

Types of String Interpolator

Scala provides three string interpolation methods out of the box: sf and raw.

1. s Interpolator

Prepending s to any string literal allows the usage of variables directly in the string. Within the String, we can access variables, object fields, functions calls, etc. You’ve already seen an example here:

val pi = 3.14  
println(s"value of pi = $pi") // value of pi = 3.14

String interpolators can also take arbitrary expressions. For example:

println(s"1 + 1 = ${1 + 1}") // 1 + 1 = 2

Any arbitrary expression can be embedded in ${}.

For some special characters, it is necessary to escape them when embedded within a string. To represent an actual dollar sign you can double it $$, like here:

println(s"New offers starting at $$15.00")

which will print the string New offers starting at $15.00.

Note: The s that’s placed before each string literal is actually a method.

2. f Interpolator

Prepending f to any string literal allows the creation of simple formatted strings, similar to printf in other languages. When using the f interpolator, all variable references should be followed by a printf-style format string, like %d. Let’s look at an example:

val height = 1.9d
val name = "James"
println(f"$name%s is $height%2.2f meters tall")  // James is 1.90 meters tall

The f interpolator is typesafe. If you try to pass a format string that only works for integers but pass a double, the compiler will issue an error. For example:

val height: Double = 1.9d

scala> f"$height%4d"
<console>:9: error: type mismatch;
 found   : Double
 required: Int
           f"$height%4d"
              ^

The f interpolator makes use of the string format utilities available from Java. The formats allowed after the % character are outlined in the Formatter javadoc. If there is no % character after a variable definition a formatter of %s (String) is assumed.

3. raw Interpolator

The raw interpolator is similar to the s interpolator except that it performs no escaping of literals within the string. Here’s an example processed string:

scala> s"a\nb"
res0: String =
a
b

Here the s string interpolator replaced the characters \n with a return character. The raw interpolator will not do that.

scala> raw"a\nb"
res1: String = a\nb

The raw interpolator is useful when you want to avoid having expressions like \n turn into a return character.
In addition to the three default string interpolators, users can define their own.

Create your own interpolator

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

s"x is $x"

is rewritten by compiler to

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

Let’s create our own string interpolator which will work as s interpolator with added some debug info to the resulting string:

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. 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

log"$logString ${1+1}"

will be rewritten by compiler to

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.

4 Comments

  1. The question that I’ve been pondering lately, is how to use string interpolators and internationalization… ideas?

Leave a Reply

%d bloggers like this: