1. Overview

In this tutorial, we’re going to see how to implement String interpolation in Scala.

String interpolation allows the evaluation of a literal string containing placeholders, yielding a string in which all the placeholders are replaced by the corresponding values.

2. s Interpolator

The s interpolator allows the usage of our variables directly in the literal string:

val age = 30
val agePresentation= s"I am $age"
// "I am 30"

We can escape the special character $ with a double $.

"It is about $$30"
// "It is about $30"

3. f Interpolator

The f interpolator is type-safe, and it’ll be familiar to Java developers formatting with printf method:

val height = 1.7d
val name = "Michele"

println(f"My name is $name%s. I am $age%d years old and $height%1.2f meters tall")
// My name is Michele. I am 30 years old and 1,70 meters tall

println(f"My name is $name%d.") 
// type safe feature: error type mismatch! 
// found : String - required: Int

All the supported formats are the same used in Java, so we can take a look at the Java format types documentation.

4. Raw Interpolator

The raw interpolator doesn’t support the escaping of literals, but works very similarly to s and f.

To better understand, let’s see an example:

raw"My name is $name%s. \n $agePresentation and $height%1.2f meters"
// My name is Michele%s. \n I am 30 and 1.7%1.2f meters

f"My name is $name%s. \n $agePresentation and $height%1.2f meters tall"
/*
My name is Michele. 
I am 30 and 1.70 meters tall
*/

5. Escaping Double Quotes in Interpolation

In this section, we’ll learn how to escape double quotes using the metacharacter ($) in the string interpolations.

First, let’s define the double quote literal in the DOUBLE_QUOTE variable:

val DOUBLE_QUOTE: Char = '"'

Now, let’s use the metacharacter ($) to include a double quote within an s-interpolated string:

val actual: String = s"Hello, ${DOUBLE_QUOTE}world!${DOUBLE_QUOTE}"
val expected = "Hello, " + DOUBLE_QUOTE + "world!" + DOUBLE_QUOTE
assert(expected == actual)

It works as expected.

Further, we must note that we can also use the double quote as it is without using the variable expression in Scala 3:

val actual: String = s"Hello, $"world!$""

Next, let’s see how to escape a double quote in an f-interpolated string:

"Escape using metachar in f-interpolation" should "include double quote character" in {
  val actual: String = f"Hello, ${DOUBLE_QUOTE}world!${DOUBLE_QUOTE}"
  val expected = "Hello, " + DOUBLE_QUOTE + "world!" + DOUBLE_QUOTE
  assert(expected == actual)
}

Great! We’ve got this one right. Like earlier, we must remember that we can achieve the same result without using the variable expression.

6. Custom Interpolator

We can also create custom interpolators, let’s take a look at a simple example that interpolate and upper case a string:

def custom(args: Any*): String = {
  
  val stringContextIterator = sc.parts.iterator
  val argsIterator = args.iterator

  val sb = new java.lang.StringBuilder(stringContextIterator.next())

  while (argsIterator.hasNext) {
    sb.append(argsIterator.next().toString)
    sb.append(stringContextIterator.next())
  }
  sb.toString.toUpperCase()
}

The implicit method on StringContext objects represent our custom interpolator, and it receives parameters to interpolate with strings. As a result, in our example we interpolate the string and upper case it.

Once we’re happy with our implementation, we can use it on any string:

val testString = "well"
custom"the custom interpolator works $testString as expected"
// THE CUSTOM INTERPOLATOR WORKS WELL AS EXPECTED

Prepending our custom interpolator to the string instance, Scala compiler will finally expand the code to:

new StringContext("the custom interpolator works ", " as expected").custom(testString)

As a result, the custom interpolation logic will interpolate the StringContext object with our testString value and upper case the string.

7. Conclusion

In this article, we’ve seen different ways of using string interpolation in Scala. We also explored how to define a custom interpolation.

Finally, the complete code for this article is available over on GitHub.