An Invitation From Scala String Interpolation


“Every generation needs a new revolution.” – Thomas Jefferson

This blog narrates the tale of an awesome change that was introduced in Scala 2.10 that made life of a Scala programmer repose and the change was the introduction of “String Interpolation” which is a mechanism that enables to embed or bind a variable references(our simple vars and vals) or a result yielding expressions(like match case, if-else, try-catch etc.) directly into a processed/unprocessed string literal.

OVERVIEW

In this blog we will learn about string interpolation; ‘s’, ‘f’ and ‘raw’ interpolators present in ‘StringContext’ class and finally how to create custom interpolators.
One major purpose of the blog is to lay a foundation for ‘Quasiqoutes’ which are implemented using string interpolation and will be covered in the next blog (If I do not get too lazy on weekends).

What does String Interpolation mean

According to Merriam Webster interpolation means – “to put (words) into a piece of writing or a conversation”
Using the statement above we can conclude that string interpolation is a mechanism that enable us to sew words(any value) in between a processed/unprocessed string literal. Here by processed we mean processing of meta-characters like escape sequences(\n, \t, \r etc.) or in other words we can say the string literal is first processed and then embedded with variable references. Consider an example where we want to print name, age, salary of an employee on the console :

val name = "LIHAS"
var age = 24
var salary = 12345.6789
println(name+" is " + age + "years old and earns ₹" + salary)

Just type the lines yourself and you will understand how troublesome it is.
To simplify this we can use a string interpolator which wave off following problems :

a). Keeping a check on opening closing double quotes
b). Repeatedly concatenating the strings to form final message.

val name = "LIHAS"
var age = 24
var salary = 12345.6789
println(s"$name is $age years old and earns ₹ $salary")

In the above example we have used the ‘s interpolator’ and the code is really elegant. We will now discuss about the three string interpolation methods provided by the ‘StringContext’ class and how they work internally and achieve this elegance.

1). The ‘s interpolator’ (The simple string interpolator) ->

As learning from the example above the ‘s interpolator’ is a mess savior. It consider standard escape sequences as defined in the Scala specification and process the string accordingly followed by inserting arguments between corresponding parts of the string. To use this interpolator just prepend the string with an ‘s’ and put a ‘$’ sign before the vars and the vals(present in the current scope) to be embedded (just take care there are no spaces between the ‘$’ and the variable). Lets learn from an example :-

val height = 8848
println("The height of Mount Everest is = $height")

OUTPUT

The height of Mount Everest is = 8848

Here, the expression ‘$height’ in the println statement is replaced with the ‘toString’ of the variable ‘height’.

Now what if we want the value returned by an expressions to be binded in the string instead. Here expression can be a match case or if-else or maybe a function call basically anything returning a value. Following example demonstrate how an expression can be embedded inside a string :-

def returnTwice(num:Int):Int = {
num * 2
}

def returnThrice(num:Int):Int = {
num * 3
}

val input = 5

println(s"Input number is $input and result is ${input match{case x if(x%2 == 0) => returnTwice(x) case x => returnThrice(x) }}")

OUTPUT

Input number is 5 and result is 15

The code demonstrated above checks whether the ‘input’ is divisible by 2 and if yes the result is twice of input else it is thrice of the input. Hence, whenever we want to interpolate an expression in a string just prepend a ‘$’ followed by ‘{}'(scope in which expression is defined). General syntax would be :-

s”${expression}”

Interpolations are the abstraction which make work of the programmer easy, let us now drill and understand what happens when ‘s interpolation’ is used. The ‘s’ which is prepended to the string literal is actually a method of ‘StringContext’ class with the following signature :-

def s(args: Any*): String = standardInterpolator(treatEscapes, args)

Here,
‘args’ are the arguments to be inserted into resulting string
‘treatescape’ is the method that process the string by applying escape sequence if any
It can be better explained by an example :-

println(s"$name is $age years old and earns ₹ $salary")

During compile time, via the compile time reflection this statement is transformed to :-

println(new StringContext(""," is ", " years old and earns ₹","").s(name,age,salary))

Here the parameters to ‘StringContext’ are referred as ‘parts’ of type ‘Seq[String]’ and parameters to ‘s’ are referred as ‘args’ of type ‘Seq[Any]’.
OUTPUT

LIHAS is 24 years old and earns ₹12345.6789

In above example firstly ‘treatEscape’ method is called on the ‘parts(0)’ and it is processed, followed by appending the ‘args(0)’ to it, then to the resultant string, processed ‘part(1)’ is appended followed by args(1) and so on. One thing to notice here is :

parts.length == args.length + 1

If the above condition is not true, ‘IllegalArgumentException’ is thrown. Now let us look at ‘raw interpolator’ provided by ‘StringContext’ which is kind of a cousin of ‘s interpolator’.

2). The ‘raw interpolator’ (The raw string interpolator) ->

The ‘raw interpolator’ is similar to ‘s interpolator’ except for one difference i.e. it do not process the string. As the name suggest(raw) it just embed the variables in a raw(unprocessed) string literal. Let us learn from an example :-

println(s"$name is $age years old \n and earns ₹ $salary") //the s interpolator

In the above example we have used ‘\n’ which is an escape sequence for new line.
OUTPUT

LIHAS is 24 years old
 and earns ₹ 12345.6789

If we use ‘raw interpolator’ , ‘\n’ will be considered as string rather than an escape sequence.

println(raw"$name is $age years old \n and earns ₹ $salary") //the raw interpolator

OUTPUT

LIHAS is 24 years old \n and earns ₹ 12345.6789

Like ‘s’, ‘raw’ is also a method of ‘StringContext’ class, difference between them can be better understood by having a glance at definition of the ‘raw interpolator’.

def raw(args: Any*): String = standardInterpolator(identity, args)

Here,
‘args’ are the arguments to be inserted into resulting string
‘identity’ is a method in ‘Predef.scala’ that returns what is gets as a parameter. And the string literal is not processed

Everything else is same in ‘s’ and ‘raw’ interpolation like the way expressions and variables are used. Time for the final interpolator provided by ‘StringContext’ i.e. the ‘f interpolator’.

3). The ‘f interpolator’ (The formatted string interpolator) ->

To understand the ‘f interpolator’, a basic knowledge about the working of ‘printf’ method is required which you can refer from [ Here ] (java based). Like the ‘s’ and the ‘raw’ interpolators, ‘f interpolator’ is also a method present in ‘StringContext’ class. Let us learn from an example :-

val name = "lihas"
val salary = 123.456
println(f"$name's salary is $salary%3.2f and will be incremented to \t ${salary + 100}%3.2f")

OUTPUT

lihas's salary is 123.46 and will be incremented to 	 223.46

From the code above 3 things can be concluded :

a). Syntax of using an ‘f interpolator’ is almost same as the ‘s’ and the ‘r’ interpolator except for the printf-style format string appended after every variable reference as well as expression.
b). Like ‘s interpolator’ the ‘f interpolator’ also process the string before embedding the variable references because the ‘\t’ results in a tab.
c). Along with variable references, value yielding expressions can also be used and must be enclosed in curly braces.

While using the ‘f interpolation’, following things must be kept in mind :

a). All variable references should be followed by a printf-style format string.

b). The f interpolator is type-safe. If you try to use a format string(like %d for integer or %s for string) that does not correspond to the type of variable reference, ‘type mismatch error’ occurs.

demo

In image above the salary is of type Double and we have used ‘%d’ (format string for integer) instead of ‘%f’ for salary, hence we get a type mismatch error

c). If there is no ‘% character’ after a variable definition a formatter of %s (String) is assumed

After discussing the string interpolation options provided to us by ‘StringContext’, let us now understand how to create a custom string interpolator.

Customizing string interpolator ->

Blog link -> My Custom String Interpolation

Due to exaggerating size of the blog, I have partitioned the explanation and example to another sub blog whose link is provided above. In this sub blog the custom interpolator being designed works exactly like the ‘s interpolator’ with an extra ability to write the post interpolation content into a file and finally returning the status message whether the file write was successful or not.
Concluding this blog, on behalf of Scala String Interpolation I invite you to explore, implement and customize the provided awesomeness.

Comments are welcomed.

References ->

1) https://github.com/scoverage/sbt-scoverage

2) http://docs.scala-lang.org/overviews/core/string-interpolation.html

3) http://www.scala-lang.org/api/current/index.html#scala.StringContext

happy coding…


KNOLDUS-advt-sticker

This entry was posted in Scala. Bookmark the permalink.

One Response to An Invitation From Scala String Interpolation

  1. Pingback: Customizing String Interpolation – An Example | Knoldus

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s