Chapter 4. Control Structures

As their name implies, control structures provide a way for programmers to control the flow of a program. They’re a fundamental feature of programming languages that let you handle decision making and looping tasks.

Prior to learning Scala back in 2010, I thought control structures like if/then statements, along with for and while loops, were relatively boring features of programming languages, but that was only because I didn’t know there was another way. These days I know that they’re a defining feature of programming languages.

Scala’s control structures are:

  • for loops and for expressions

  • if/then/else if expressions

  • match expressions (pattern matching)

  • try/catch/finally blocks

  • while loops

I’ll briefly introduce each of these next, and then the recipes will show you additional details about how to use their features.

for Loops and for Expressions

In their most basic use, for loops provide a way to iterate over a collection to operate on the collection’s elements:

for i <- List(1, 2, 3) do println(i)

But that’s just a basic use case. for loops can also have guards—embedded if statements:

for
    i <- 1 to 10
    if i > 3
    if i < 6
do
    println(i)

With the use of the yield keyword, for loops also become for expressions—loops that yield a result:

val listOfInts = for
    i <- 1 to 10
    if i > 3
    if i < 6
yield
    i * 10

After that loop runs, listOfInts is a Vector(40, 50). The guards inside the loop filter out all of the values except 4 and 5, and then those values are multiplied by 10 in the yield block.

Many more details about for loops and expressions are covered in the initial recipes in this chapter.

if/then/else-if Expressions

While for loops and expressions let you traverse over a collection, if/then/else expressions provide a way to make branching decisions. In Scala 3 the preferred syntax has changed, and now looks like this:

val absValue = if a < 0 then -a else a

def compare(a: Int, b: Int): Int =
    if a < b then
        -1
    else if a == b then
        0
    else
        1
end compare

As shown in both of those examples, an if expression truly is an expression that returns a value. (Expressions are discussed in Recipe 4.5.)

match Expressions and Pattern Matching

Next, match expressions and pattern matching are a defining feature of Scala, and demonstrating their capabilities takes up the majority of this chapter. Like if expressions, match expressions return values, so you can use them as the body of a method. As an example, this method is similar to the Perl programming language’s version of things that are true and false:

def isTrue(a: Matchable): Boolean = a match
    case false | 0 | "" => false
    case _ => true

In that code, if isTrue receives a 0 or an empty string, it returns false, otherwise it returns true. Ten recipes in this chapter are used to detail the features of match expressions.

try/catch/finally Blocks

Next, Scala’s try/catch/finally blocks are similar to Java, but the syntax is slightly different, primarily in that the catch block is consistent with a match expression:

try
    // some exception-throwing code here
catch
    case e1: Exception1Type => // handle that exception
    case e2: Exception2Type => // handle that exception
finally
    // close your resources and do anything else necessary here

Like if and match, try is an expression that returns a value, so you can write code like this to transform a String into an Int:

def toInt(s: String): Option[Int] =
    try
        Some(s.toInt)
    catch
        case e: NumberFormatException => None

These examples show how toInt works:

toInt("1")    // Option[Int] = Some(1)
toInt("Yo")   // Option[Int] = None

Recipe 4.16 provides more information about try/catch blocks.

while Loops

When it comes to while loops, you’ll find that they’re rarely used in Scala. This is because while loops are mostly used for side effects, such as updating mutable variables and printing with println, and these are things you can also do with for loops and the foreach method on collections. That being said, if you ever need to use one, their syntax looks like this:

while
    i < 10
do
    println(i)
    i += 1

while loops are briefly covered in Recipe 4.1.

Finally, because of a combination of several Scala features, you can create your own control structures, and these capabilities are discussed in Recipe 4.17.

Control Structures as a Defining Feature of Programming Languages

At the end of 2020 I was fortunate enough to cowrite the Scala 3 Book on the official Scala Documentation website, including these three chapters:

When I said earlier that control structures are a “defining feature of programming languages,” one of the things I meant is that after I wrote those chapters, I came to realize the power of the features in this chapter, as well as how consistent Scala is compared to other programming languages. That consistency is one of the features that makes Scala a joy to use.

4.1 Looping over Data Structures with for

Problem

You want to iterate over the elements in a collection in the manner of a traditional for loop.

Solution

There are many ways to loop over Scala collections, including for loops, while loops, and collection methods like foreach, map, flatMap, and more. This solution focuses primarily on the for loop.

Given a simple list:

val fruits = List("apple", "banana", "orange")

you can loop over the elements in the list and print them like this:

scala> for f <- fruits do println(f)
apple
banana
orange

That same approach works for all sequences, including List, Seq, Vector, Array, ArrayBuffer, etc.

When your algorithm requires multiple lines, use the same for loop syntax, and perform your work in a block inside curly braces:

scala> for f <- fruits do
     |      // imagine this requires multiple lines
     |      val s = f.toUpperCase
     |      println(s)
APPLE
BANANA
ORANGE

for loop counters

If you need access to a counter inside a for loop, use one of the following approaches. First, you can access the elements in a sequence with a counter like this:

for i <- 0 until fruits.length do
    println(s"$i is ${fruits(i)}")

That loops yields this output:

0 is apple
1 is banana
2 is orange

You rarely need to access sequence elements by their index, but when you do, that is one possible approach. Scala collections also offer a zipWithIndex method that you can use to create a loop counter:

for (fruit, index) <- fruits.zipWithIndex do
    println(s"$index is $fruit")

Its output is:

0 is apple
1 is banana
2 is orange

Generators

On a related note, the following example shows how to use a Range to execute a loop three times:

scala> for i <- 1 to 3 do println(i)
1
2
3

The 1 to 3 portion of the loop creates a Range, as shown in the REPL:

scala> 1 to 3
res0: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3)

Using a Range like this is known as using a generator. Recipe 4.2 demonstrates how to use this technique to create multiple loop counters.

Looping over a Map

When iterating over keys and values in a Map, I find this to be the most concise and readable for loop:

val names = Map(
    "firstName" -> "Robert",
    "lastName" -> "Goren"
)

for (k,v) <- names do println(s"key: $k, value: $v")

The REPL shows its output:

scala> for (k,v) <- names do println(s"key: $k, value: $v")
key: firstName, value: Robert
key: lastName, value: Goren

Discussion

Because I’ve switched to a functional programming style, I haven’t used a while loop in several years, but the REPL demonstrates how it works:

scala> var i = 0
i: Int = 0

scala> while i < 3 do
     |     println(i)
     |     i += 1
0
1
2

while loops are generally used for side effects, such as updating a mutable variable like i and writing output to the outside world. As my code gets closer to pure functional programming—where there is no mutable state—I haven’t had any need for them.

That being said, when you’re programming in an object-oriented programming style, while loops are still frequently used, and that example demonstrates their syntax. A while loop can also be written on multiple lines like this:

while
    i < 10
do
    println(i)
    i += 1

Collection methods like foreach

In some ways Scala reminds me of the Perl slogan, “There’s more than one way to do it,” and iterating over a collection provides some great examples of this. With the wealth of methods that are available on collections, it’s important to note that a for loop may not even be the best approach to a particular problem; the methods foreach, map, flatMap, collect, reduce, etc., can often be used to solve your problem without requiring an explicit for loop.

For example, when you’re working with a collection, you can also iterate over each element by calling the foreach method on the collection:

scala> fruits.foreach(println)
apple
banana
orange

When you have an algorithm you want to run on each element in the collection, just pass the anonymous function into foreach:

scala> fruits.foreach(e => println(e.toUpperCase))
APPLE
BANANA
ORANGE

As with the for loop, if your algorithm requires multiple lines, perform your work in a block:

scala> fruits.foreach { e =>
     |     val s = e.toUpperCase
     |     println(s)
     | }
APPLE
BANANA
ORANGE

See Also

The theory behind how for loops work is very interesting, and knowing it can be helpful as you progress. I wrote about it at length in these articles:

4.2 Using for Loops with Multiple Counters

Problem

You want to create a loop with multiple counters, such as when iterating over a multidimensional array.

Solution

You can create a for loop with two counters like this:

scala> for i <- 1 to 2; j <- 1 to 2 do println(s"i = $i, j = $j")
i = 1, j = 1
i = 1, j = 2
i = 2, j = 1
i = 2, j = 2

Notice that it sets i to 1, loops through the elements in j, then sets i to 2 and repeats the process.

Using that approach works well with small examples, but when your code gets larger, this is the preferred style:

for
    i <- 1 to 3
    j <- 1 to 5
    k <- 1 to 10 by 2
do
    println(s"i = $i, j = $j, k = $k")

This approach is useful when looping over a multidimensional array. Assuming you create and populate a small two-dimensional array like this:

val a = Array.ofDim[Int](2,2)
a(0)(0) = 0
a(0)(1) = 1
a(1)(0) = 2
a(1)(1) = 3

you can print every array element like this:

scala> for
     |     i <- 0 to 1
     |     j <- 0 to 1
     | do
     |     println(s"($i)($j) = ${a(i)(j)}")
(0)(0) = 0
(0)(1) = 1
(1)(0) = 2
(1)(1) = 3

Discussion

As shown in Recipe 15.2, “Creating Ranges”, the 1 to 5 syntax creates a Range:

scala> 1 to 5
val res0: scala.collection.immutable.Range.Inclusive = Range 1 to 5

Ranges are great for many purposes, and ranges created with the <- symbol in for loops are referred to as generators. As shown, you can easily use multiple generators in one loop.

4.3 Using a for Loop with Embedded if Statements (Guards)

Problem

You want to add one or more conditional clauses to a for loop, typically to filter out some elements in a collection while working on the others.

Solution

Add one or more if statements after your generator, like this:

for
    i <- 1 to 10
    if i % 2 == 0
do
    print(s"$i ")

// output: 2 4 6 8 10

These if statements are referred to as filters, filter expressions, or guards, and you can use as many guards as needed for the problem at hand. This loop shows a hard way to print the number 4:

for
    i <- 1 to 10
    if i > 3
    if i < 6
    if i % 2 == 0
do
    println(i)

Discussion

It’s still possible to write for loops with if expressions in an older style. For instance, given this code:

import java.io.File
val dir = File(".")
val files: Array[java.io.File] = dir.listFiles()

you could, in theory, write a for loop in a style like this, which is reminiscent of C and Java:

// a C/Java style of writing a 'for' loop
for (file <- files) {
    if (file.isFile && file.getName.endsWith(".scala")) {
        println(s"Scala file: $file")
    }
}

However, once you become comfortable with Scala’s for loop syntax, I think you’ll find that it makes the code more readable, because it separates the looping and filtering concerns from the business logic:

for
    // loop and filter
    file <- files
    if file.isFile
    if file.getName.endsWith(".scala")
do
    // as much business logic here as needed
    println(s"Scala file: $file")

Note that because guards are generally intended to filter collections, you may want to use one of the many filtering methods that are available to collections—filter, take, drop, etc.—instead of a for loop, depending on your needs. See Chapter 11 for examples of those methods.

4.4 Creating a New Collection from an Existing Collection with for/yield

Problem

You want to create a new collection from an existing collection by applying an algorithm (and potentially one or more guards) to each element in the original collection.

Solution

Use a yield statement with a for loop to create a new collection from an existing collection. For instance, given an array of lowercase strings:

scala> val names = List("chris", "ed", "maurice")
val names: List[String] = List(chris, ed, maurice)

you can create a new array of capitalized strings by combining yield with a for loop and a simple algorithm:

scala> val capNames = for name <- names yield name.capitalize
val capNames: List[String] = List(Chris, Ed, Maurice)

Using a for loop with a yield statement is known as a for-comprehension.

If your algorithm requires multiple lines of code, perform the work in a block after the yield keyword, manually specifying the type of the resulting variable, or not:

// [1] declare the type of `lengths`
val lengths: List[Int] = for name <- names yield
    // imagine that this body requires multiple lines of code
    name.length

// [2] don’t declare the type of `lengths`
val lengths = for name <- names yield
    // imagine that this body requires multiple lines of code
    name.length

Both approaches yield the same result:

List[Int] = List(5, 2, 7)

Both parts of your for comprehension (also known as a for expression) can be as complicated as necessary. Here’s a larger example:

val xs = List(1,2,3)
val ys = List(4,5,6)
val zs = List(7,8,9)

val a = for
    x <- xs
    if x > 2
    y <- ys
    z <- zs
    if y * z < 45
yield
    val b = x + y
    val c = b * z
    c

That for comprehension yields the following result:

a: List[Int] = List(49, 56, 63, 56, 64, 63)

A for comprehension can even be the complete body of a method:

def between3and10(xs: List[Int]): List[Int] =
    for
        x <- xs
        if x >= 3
        if x <= 10
    yield x

between3and10(List(1,3,7,11))   // List(3, 7)

Discussion

If you’re new to using yield with a for loop, it can help to think of the loop like this:

  1. When it begins running, the for/yield loop immediately creates a new empty collection that is of the same type as the input collection. For example, if the input type is a Vector, the output type will also be a Vector. You can think of this new collection as being like an empty bucket.

  2. On each iteration of the for loop, a new output element may be created from the current element of the input collection. When the output element is created, it’s placed in the bucket.

  3. When the loop finishes running, the entire contents of the bucket are returned.

That’s a simplification, but I find it helpful when explaining the process.

Note that writing a for expression without a guard is just like calling the map method on a collection.

For instance, the following for comprehension converts all the strings in the fruits collection to uppercase:

scala> val namesUpper = for n <- names yield n.toUpperCase
val namesUpper: List[String] = List(CHRIS, ED, MAURICE)

Calling the map method on the collection does the same thing:

scala> val namesUpper = names.map(_.toUpperCase)
val namesUpper: List[String] = List(CHRIS, ED, MAURICE)

When I first started learning Scala, I wrote all of my code using for/yield expressions, until one day I realized that using for/yield without a guard was the same as using map.

See Also

4.5 Using the if Construct Like a Ternary Operator

Problem

You’re familiar with Java’s special ternary operator syntax:

int absValue = (a < 0) ? -a : a;

and you’d like to know what the Scala equivalent is.

Solution

This is a bit of a trick problem, because unlike Java, in Scala there is no special ternary operator; just use an if/else/then expression:

val a = 1
val absValue = if a < 0 then -a else a

Because an if expression returns a value, you can embed it into a print statement:

println(if a == 0 then "a" else "b")

You can also use it in another expression, such as this portion of a hashCode method:

hash = hash * prime + (if name == null then 0 else name.hashCode)

The fact that if/else expressions return a value also lets you write concise methods:

// Version 1: one-line style
def abs(x: Int) = if x >= 0 then x else -x
def max(a: Int, b: Int) = if a > b then a else b

// Version 2: the method body on a separate line, if you prefer
def abs(x: Int) =
    if x >= 0 then x else -x

def max(a: Int, b: Int) =
    if a > b then a else b

Discussion

The “Equality, Relational, and Conditional Operators” Java documentation page states that the Java conditional operator ?: “is known as the ternary operator because it uses three operands.”

Java requires a separate syntax here because the Java if/else construct is a statement; it doesn’t have a return value, and is only used for side effects, such as updating mutable fields. Conversely, because Scala’s if/else/then truly is an expression, a special operator isn’t needed. See Recipe 24.3, “Writing Expressions (Instead of Statements)”, for more details on statements and expressions.

Arity

The word ternary has to do with the arity of functions. Wikipedia’s “Arity” page states, “In logic, mathematics, and computer science, the arity of a function or operation is the number of arguments or operands that the function takes.” A unary operator takes one operand, a binary operator takes two operands, and a ternary operator takes three operands.

4.6 Using a Match Expression Like a switch Statement

Problem

You have a situation where you want to create something like a simple Java integer-based switch statement, such as matching the days in a week, the months in a year, and other situations where an integer maps to a result.

Solution

To use a Scala match expression like a simple, integer-based switch statement, use this approach:

import scala.annotation.switch

// `i` is an integer
(i: @switch) match
    case 0 => println("Sunday")
    case 1 => println("Monday")
    case 2 => println("Tuesday")
    case 3 => println("Wednesday")
    case 4 => println("Thursday")
    case 5 => println("Friday")
    case 6 => println("Saturday")
    // catch the default with a variable so you can print it
    case whoa  => println(s"Unexpected case: ${whoa.toString}")

That example shows how to produce a side-effect action (println) based on a match. A more functional approach is to return a value from a match expression:

import scala.annotation.switch

// `i` is an integer
val day = (i: @switch) match
    case 0 => "Sunday"
    case 1 => "Monday"
    case 2 => "Tuesday"
    case 3 => "Wednesday"
    case 4 => "Thursday"
    case 5 => "Friday"
    case 6 => "Saturday"
    case _ => "invalid day"   // the default, catch-all

The @switch annotation

When writing simple match expressions like this, it’s recommended to use the @switch annotation, as shown. This annotation provides a warning at compile time if the switch can’t be compiled to a tableswitch or lookupswitch. Compiling your match expression to a tableswitch or lookupswitch is better for performance because it results in a branch table rather than a decision tree. When a value is given to the expression, it can jump directly to the result rather than working through the decision tree.

The Scala @switch annotation documentation states:

If [this annotation is] present, the compiler will verify that the match has been compiled to a tableswitch or lookupswitch, and issue an error if it instead compiles into a series of conditional expressions

The effect of the @switch annotation is demonstrated with a simple example. First, place the following code in a file named SwitchDemo.scala:

// Version 1 - compiles to a tableswitch
import scala.annotation.switch

class SwitchDemo:
    val i = 1
    val x = (i: @switch) match
        case 1 => "One"
        case 2 => "Two"
        case 3 => "Three"
        case _ => "Other"

Then compile the code as usual:

$ scalac SwitchDemo.scala

Compiling this class produces no warnings and creates the SwitchDemo.class output file. Next, disassemble that file with this javap command:

$ javap -c SwitchDemo

The output from this command shows a tableswitch, like this:

16: tableswitch   { // 1 to 3
             1: 44
             2: 52
             3: 60
       default: 68
  }

This shows that Scala was able to optimize your match expression to a tableswitch. (This is a good thing.)

Next, make a minor change to the code, replacing the integer literal 1 with a value:

import scala.annotation.switch

// Version 2 - leads to a compiler warning
class SwitchDemo:
    val i = 1
    val one = 1               // added
    val x = (i: @switch) match
        case one  => "One"    // replaced the '1'
        case 2    => "Two"
        case 3    => "Three"
        case _    => "Other"

Again, compile the code with scalac, but right away you’ll see a warning message:

$ scalac SwitchDemo.scala
SwitchDemo.scala:7: warning: could not emit switch for @switch annotated match
  val x = (i: @switch) match {
               ^
one warning found

This warning message means that neither a tableswitch nor a lookupswitch could be generated for the match expression. You can confirm this by running the javap command on the SwitchDemo.class file that was generated. When you look at that output, you’ll see that the tableswitch shown in the previous example is now gone.

In his book, Scala in Depth (Manning), Joshua Suereth states that the following conditions must be true for Scala to apply the tableswitch optimization:

  • The matched value must be a known integer.

  • The matched expression must be “simple.” It can’t contain any type checks, if statements, or extractors.

  • The expression must have its value available at compile time.

  • There should be more than two case statements.

Discussion

The examples in the Solution showed the two ways you can handle the default “catch all” case. First, if you’re not concerned about the value of the default match, you can catch it with the _ wildcard:

case _ => println("Got a default match")

Conversely, if you are interested in what fell down to the default match, assign a variable name to it. You can then use that variable on the right side of the expression:

case default => println(default)

Using a name like default often makes the most sense, but you can use any legal name for the variable:

case oops => println(oops)

It’s important to know that you can generate a MatchError if you don’t handle the default case. Given this match expression:

i match
    case 0 => println("0 received")
    case 1 => println("1 is good, too")

if i is a value other than 0 or 1, the expression throws a MatchError:

scala.MatchError: 42 (of class java.lang.Integer)
  at .<init>(<console>:9)
  at .<clinit>(<console>)
    much more error output here ...

So unless you’re intentionally writing a partial function, you’ll want to handle the default case.

Do you really need a match expression?

Note that you may not need a match expression for examples like this. For instance, any time you’re just mapping one value to another, it may be preferable to use a Map:

val days = Map(
    0 -> "Sunday",
    1 -> "Monday",
    2 -> "Tuesday",
    3 -> "Wednesday",
    4 -> "Thursday",
    5 -> "Friday",
    6 -> "Saturday"
)

println(days(0))   // prints "Sunday"

See Also

4.7 Matching Multiple Conditions with One Case Statement

Problem

You have a situation where several match conditions require that the same business logic be executed, and rather than repeating your business logic for each case, you’d like to use one copy of the business logic for the matching conditions.

Solution

Place the match conditions that invoke the same business logic on one line, separated by the | (pipe) character:

// `i` is an Int
i match
    case 1 | 3 | 5 | 7 | 9 => println("odd")
    case 2 | 4 | 6 | 8 | 10 => println("even")
    case _ => println("too big")

This same syntax works with strings and other types. Here’s an example based on a String match:

val cmd = "stop"
cmd match
    case "start" | "go" => println("starting")
    case "stop" | "quit" | "exit" => println("stopping")
    case _ => println("doing nothing")

This example shows how to match multiple objects on each case statement:

enum Command:
    case Start, Go, Stop, Whoa

import Command.*
def executeCommand(cmd: Command): Unit = cmd match
    case Start | Go => println("start")
    case Stop | Whoa => println("stop")

As demonstrated, the ability to define multiple possible matches for each case statement can simplify your code.

See Also

4.8 Assigning the Result of a Match Expression to a Variable

Problem

You want to return a value from a match expression and assign it to a variable, or use a match expression as the body of a method.

Solution

To assign the result of a match expression to a variable, insert the variable assignment before the expression, as with the variable evenOrOdd in this example:

val someNumber = scala.util.Random.nextInt()
val evenOrOdd = someNumber match
    case 1 | 3 | 5 | 7 | 9 => "odd"
    case 2 | 4 | 6 | 8 | 10 => "even"
    case _ => "other"

This approach is commonly used to create short methods or functions. For example, the following method implements the Perl definitions of true and false:

def isTrue(a: Matchable): Boolean = a match
    case false | 0 | "" => false
    case _ => true

Discussion

You may hear that Scala is an expression-oriented programming (EOP) language. EOP means that every construct is an expression, yields a value, and doesn’t have a side effect. Unlike other languages, in Scala every construct like if, match, for, and try returns a value. See Recipe 24.3, “Writing Expressions (Instead of Statements)”, for more details.

4.9 Accessing the Value of the Default Case in a Match Expression

Problem

You want to access the value of the default “catch all” case when using a match expression, but you can’t access the value when you match it with the _ wildcard syntax.

Solution

Instead of using the _ wildcard character, assign a variable name to the default case:

i match
    case 0 => println("1")
    case 1 => println("2")
    case default => println(s"You gave me: $default")

By giving the default match a variable name, you can access the variable on the right side of the expression.

Discussion

The key to this recipe is in using a variable name for the default match instead of the usual _ wildcard character. The name you assign can be any legal variable name, so instead of naming it default, you can name it something else, such as what:

i match
    case 0 => println("1")
    case 1 => println("2")
    case what => println(s"You gave me: $what" )

It’s important to provide a default match. Failure to do so can cause a MatchError:

scala> 3 match
     |     case 1 => println("one")
     |     case 2 => println("two")
     |     // no default match
scala.MatchError: 3 (of class java.lang.Integer)
many more lines of output ...

See the Discussion of Recipe 4.6 for more MatchError details.

4.10 Using Pattern Matching in Match Expressions

Problem

You need to match one or more patterns in a match expression, and the pattern may be a constant pattern, variable pattern, constructor pattern, sequence pattern, tuple pattern, or type pattern.

Solution

Define a case statement for each pattern you want to match. The following method shows examples of many different types of patterns you can use in match expressions:

def test(x: Matchable): String = x match

    // constant patterns
    case 0 => "zero"
    case true => "true"
    case "hello" => "you said 'hello'"
    case Nil => "an empty List"

    // sequence patterns
    case List(0, _, _) => "a 3-element list with 0 as the first element"
    case List(1, _*) => "list, starts with 1, has any number of elements"

    // tuples
    case (a, b) => s"got $a and $b"
    case (a, b, c) => s"got $a, $b, and $c"

    // constructor patterns
    case Person(first, "Alexander") => s"Alexander, first name = $first"
    case Dog("Zeus") => "found a dog named Zeus"

    // typed patterns
    case s: String => s"got a string: $s"
    case i: Int => s"got an int: $i"
    case f: Float => s"got a float: $f"
    case a: Array[Int] => s"array of int: ${a.mkString(",")}"
    case as: Array[String] => s"string array: ${as.mkString(",")}"
    case d: Dog => s"dog: ${d.name}"
    case list: List[_] => s"got a List: $list"
    case m: Map[_, _] => m.toString

    // the default wildcard pattern
    case _ => "Unknown"

end test

The large match expression in this method shows the different categories of patterns described in the book Programming in Scala, including constant patterns, sequence patterns, tuple patterns, constructor patterns, and typed patterns.

The following code demonstrates all of the cases in the match expression, with the output of each expression shown in the comments. Note that the println method is renamed on import to make the examples more concise:

import System.out.{println => p}

case class Person(firstName: String, lastName: String)
case class Dog(name: String)

// trigger the constant patterns
p(test(0))               // zero
p(test(true))            // true
p(test("hello"))         // you said 'hello'
p(test(Nil))             // an empty List

// trigger the sequence patterns
p(test(List(0,1,2)))     // a 3-element list with 0 as the first element
p(test(List(1,2)))       // list, starts with 1, has any number of elements
p(test(List(1,2,3)))     // list, starts with 1, has any number of elements
p(test(Vector(1,2,3)))   // vector, starts w/ 1, has any number of elements

// trigger the tuple patterns
p(test((1,2)))                            // got 1 and 2
p(test((1,2,3)))                          // got 1, 2, and 3

// trigger the constructor patterns
p(test(Person("Melissa", "Alexander")))   // Alexander, first name = Melissa
p(test(Dog("Zeus")))                      // found a dog named Zeus

// trigger the typed patterns
p(test("Hello, world"))                   // got a string: Hello, world
p(test(42))                               // got an int: 42
p(test(42F))                              // got a float: 42.0
p(test(Array(1,2,3)))                     // array of int: 1,2,3
p(test(Array("coffee", "apple pie")))     // string array: coffee,apple pie
p(test(Dog("Fido")))                      // dog: Fido
p(test(List("apple", "banana")))          // got a List: List(apple, banana)
p(test(Map(1->"Al", 2->"Alexander")))     // Map(1 -> Al, 2 -> Alexander)

// trigger the wildcard pattern
p(test("33d"))                            // you gave me this string: 33d

Note that in the match expression, the List and Map expressions that were written like this:

case m: Map[_, _] => m.toString
case list: List[_] => s"thanks for the List: $list"

could have been written as this instead:

case m: Map[A, B] => m.toString
case list: List[X] => s"thanks for the List: $list"

I prefer the underscore syntax because it makes it clear that I’m not concerned about what’s stored in the List or Map. Actually, there are times that I might be interested in what’s stored in the List or Map, but because of type erasure in the JVM, that becomes a difficult problem.

Type Erasure

When I first wrote this example, I wrote the List expression as follows:

case l: List[Int] => "List"

If you’re familiar with type erasure on the Java platform, you may know that this won’t work. The Scala compiler kindly lets you know about this problem with this warning message:

Test1.scala:7: warning: non-variable type argument Int in
type pattern List[Int] is unchecked since it is eliminated
by erasure case l: List[Int] => "List[Int]"
                   ^

If you’re not familiar with type erasure, I’ve included a link in the See Also section of this recipe to a page that describes how it works on the JVM.

Discussion

Typically, when using this technique, your method will expect an instance that inherits from a base class or trait, and then your case statements will reference subtypes of that base type. This was inferred in the test method, where every Scala type is a subtype of Matchable. The following code shows a more obvious example.

In my Blue Parrot application, which either plays a sound file or “speaks” the text it’s given at random time intervals, I have a method that looks like this:

import java.io.File

sealed trait RandomThing

case class RandomFile(f: File) extends RandomThing
case class RandomString(s: String) extends RandomThing

class RandomNoiseMaker:
    def makeRandomNoise(thing: RandomThing) = thing match
        case RandomFile(f) => playSoundFile(f)
        case RandomString(s) => speakText(s)

The makeRandomNoise method is declared to take a RandomThing type, and then the match expression handles its two subtypes, RandomFile and RandomString.

Patterns

The large match expression in the Solution shows a variety of patterns that are defined in the book Programming in Scala (which was cowritten by Martin Odersky, the creator of the Scala language). The patterns include:

  • Constant patterns

  • Variable patterns

  • Constructor patterns

  • Sequence patterns

  • Tuple patterns

  • Typed patterns

  • Variable-binding patterns

These patterns are briefly described in the following paragraphs.

Constant patterns

A constant pattern can only match itself. Any literal may be used as a constant. If you specify a 0 as the literal, only an Int value of 0 will be matched. Examples include:

case 0 => "zero"
case true => "true"
Variable patterns

This was not shown in the large match example in the Solution, but a variable pattern matches any object, just like the _ wildcard character. Scala binds the variable to whatever the object is, which lets you use the variable on the right side of the case statement. For example, at the end of a match expression you can use the _ wildcard character like this to catch anything else:

case _ => s"Hmm, you gave me something ..."

But with a variable pattern you can write this instead:

case foo => s"Hmm, you gave me a $foo"

See Recipe 4.9 for more information.

Constructor patterns

The constructor pattern lets you match a constructor in a case statement. As shown in the examples, you can specify constants or variable patterns as needed in the constructor pattern:

case Person(first, "Alexander") => s"found an Alexander, first name = $first"
case Dog("Zeus") => "found a dog named Zeus"
Sequence patterns

You can match against sequences like List, Array, Vector, etc. Use the _ character to stand for one element in the sequence, and use _* to stand for zero or more elements, as shown in the examples:

case List(0, _, _) => "a 3-element list with 0 as the first element"
case List(1, _*) => "list, starts with 1, has any number of elements"
case Vector(1, _*) => "vector, starts with 1, has any number of elements"
Tuple patterns

As shown in the examples, you can match tuple patterns and access the value of each element in the tuple. You can also use the _ wildcard if you’re not interested in the value of an element:

case (a, b, c) => s"3-elem tuple, with values $a, $b, and $c"
case (a, b, c, _) => s"4-elem tuple: got $a, $b, and $c"
Typed patterns

In the following example, str: String is a typed pattern, and str is a pattern variable:

case str: String => s"you gave me this string: $str"

As shown in the examples, you can access the pattern variable on the right side of the expression after declaring it.

Variable-binding patterns

At times you may want to add a variable to a pattern. You can do this with the following general syntax:

case variableName @ pattern => ...

This is called a variable-binding pattern. When it’s used, the input variable to the match expression is compared to the pattern, and if it matches, the input variable is bound to variableName.

The usefulness of this is best shown by demonstrating the problem it solves. Suppose you had the List pattern that was shown earlier:

case List(1, _*) => "a list beginning with 1, having any number of elements"

As demonstrated, this lets you match a List whose first element is 1, but so far, the List hasn’t been accessed on the right side of the expression. When accessing a List, you know that you can do this:

case list: List[_] => s"thanks for the List: $list"

so it seems like you should try this with a sequence pattern:

case list: List(1, _*) => s"thanks for the List: $list"

Unfortunately, this fails with the following compiler error:

Test2.scala:22: error: '=>' expected but '(' found.
    case list: List(1, _*) => s"thanks for the List: $list"
                   ^
one error found

The solution to this problem is to add a variable-binding pattern to the sequence pattern:

case list @ List(1, _*) => s"$list"

This code compiles, and works as expected, giving you access to the List on the right side of the statement.

The following code demonstrates this example and the usefulness of this approach:

case class Person(firstName: String, lastName: String)

def matchType(x: Matchable): String = x match
    //case x: List(1, _*) => s"$x"   // doesn’t compile
    case x @ List(1, _*) => s"$x"    // prints the list

    //case Some(_) => "got a Some"   // works, but can’t access the Some
    //case Some(x) => s"$x"          // returns "foo"
    case x @ Some(_) => s"$x"        // returns "Some(foo)"

    case p @ Person(first, "Doe") => s"$p" // returns "Person(John,Doe)"
end matchType

@main def test2 =
    println(matchType(List(1,2,3)))             // prints "List(1, 2, 3)"
    println(matchType(Some("foo")))             // prints "Some(foo)"
    println(matchType(Person("John", "Doe")))   // prints "Person(John,Doe)"

In the two List examples inside the match expression, the commented-out line of code won’t compile, but the second line shows how to match the desired List object and then bind that list to the variable x. When this line of code matches a list like List(1,2,3), it results in the output List(1, 2, 3), as shown in the output of the first println statement.

The first Some example shows that you can match a Some with the approach shown, but you can’t access its information on the right side of the expression. The second example shows how you can access the value inside the Some, and the third example takes this a step further, giving you access to the Some object itself. When it’s matched by the second println call, it prints Some(foo), demonstrating that you now have access to the Some object.

Finally, this approach is used to match a Person whose last name is Doe. This syntax lets you assign the result of the pattern match to the variable p, and then access that variable on the right side of the expression.

Using Some and None in match expressions

To round out these examples, you’ll often use Some and None with match expressions. For instance, when you attempt to create a number from a string with a method like toIntOption, you can handle the result in a match expression:

val s = "42"

// later in the code ...
s.toIntOption match
    case Some(i) => println(i)
    case None => println("That wasn't an Int")

Inside the match expression you just specify the Some and None cases as shown to handle the success and failure conditions. See Recipe 24.6, “Using Scala’s Error-Handling Types (Option, Try, and Either), for more examples of using Option, Some, and None.

See Also

4.11 Using Enums and Case Classes in match Expressions

Problem

You want to match enums, case classes, or case objects in a match expression.

Solution

The following example demonstrates how to use patterns to match enums in different ways, depending on what information you need on the right side of each case statement. First, here’s an enum named Animal that has three instances, Dog, Cat, and Woodpecker:

enum Animal:
    case Dog(name: String)
    case Cat(name: String)
    case Woodpecker

Given that enum, this getInfo method shows the different ways you can match the enum types in a match expression:

import Animal.*

def getInfo(a: Animal): String = a match
    case Dog(moniker) => s"Got a Dog, name = $moniker"
    case _: Cat       => "Got a Cat (ignoring the name)"
    case Woodpecker   => "That was a Woodpecker"

These examples show how getInfo works when given a Dog, Cat, and Woodpecker:

println(getInfo(Dog("Fido")))     // Got a Dog, name = Fido
println(getInfo(Cat("Morris")))   // Got a Cat (ignoring the name)
println(getInfo(Woodpecker))      // That was a Woodpecker

In getInfo, if the Dog class is matched, its name is extracted and used to create the string on the right side of the expression. To show that the variable name used when extracting the name can be any legal variable name, I use the name moniker.

When matching a Cat I want to ignore the name, so I use the syntax shown to match any Cat instance. Because Woodpecker isn’t created with a parameter, it’s also matched as shown.

Discussion

In Scala 2, sealed traits were used with case classes and case objects to achieve the same effect as the enum:

sealed trait Animal
case class Dog(name: String) extends Animal
case class Cat(name: String) extends Animal
case object Woodpecker extends Animal

As described in Recipe 6.12, “How to Create Sets of Named Values with Enums”, an enum is a shortcut for defining (a) a sealed class or trait along with (b) values defined as members of the class’s companion object. Both approaches can be used in the match expression in getInfo because case classes have a built-in unapply method, which lets them work in match expressions. I describe how this works in Recipe 7.8, “Implementing Pattern Matching with unapply”.

4.12 Adding if Expressions (Guards) to Case Statements

Problem

You want to add qualifying logic to a case statement in a match expression, such as allowing a range of numbers or matching a pattern, but only if that pattern matches some additional criteria.

Solution

Add an if guard to your case statement. Use it to match a range of numbers:

i match
    case a if 0 to 9 contains a => println("0-9 range: " + a)
    case b if 10 to 19 contains b => println("10-19 range: " + b)
    case c if 20 to 29 contains c => println("20-29 range: " + c)
    case _ => println("Hmmm...")

Use it to match different values of an object:

i match
    case x if x == 1 => println("one, a lonely number")
    case x if (x == 2 || x == 3) => println(x)
    case _ => println("some other value")

As long as your class has an unapply method, you can reference class fields in your if guards. For instance, because a case class has an automatically generated unapply method, given this Stock class and instance:

case class Stock(symbol: String, price: BigDecimal)
val stock = Stock("AAPL", BigDecimal(132.50))

you can use pattern matching and guard conditions with the class fields:

stock match
    case s if s.symbol == "AAPL" && s.price < 140 => buy(s)
    case s if s.symbol == "AAPL" && s.price > 160 => sell(s)
    case _ => // do nothing

You can also extract fields from case classes—and classes that have properly implemented unapply methods—and use those in your guard conditions. For example, the case statements in this match expression:

// extract the 'name' in the 'case' and then use that value
def speak(p: Person): Unit = p match
    case Person(name) if name == "Fred" =>
        println("Yabba dabba doo")
    case Person(name) if name == "Bam Bam" =>
        println("Bam bam!")
    case _ =>
        println("Watch the Flintstones!")

will work if Person is defined as a case class:

case class Person(aName: String)

or as a class with a properly implemented unapply method:

class Person(val aName: String)
object Person:
    // 'unapply' deconstructs a Person. it’s also known as an
    // extractor, and Person is an “extractor object.”
    def unapply(p: Person): Option[String] = Some(p.aName)

See Recipe 7.8, “Implementing Pattern Matching with unapply”, for more details on how to write unapply methods.

Discussion

You can use if expressions like this whenever you want to add boolean tests to the left side of case statements (i.e., before the => symbol).

Note that all of these examples could be written by putting the if tests on the right side of the expressions, like this:

case Person(name) =>
    if name == "Fred" then println("Yabba dabba doo")
    else if name == "Bam Bam" then println("Bam bam!")

However, for many situations, your code will be simpler and easier to read by joining the if guard directly with the case statement; it helps to separate the guard from the later business logic.

Also note that this Person example is a little contrived, because Scala’s pattern-matching capabilities let you write the cases like this:

def speak(p: Person): Unit = p match
    case Person("Fred") => println("Yabba dabba doo")
    case Person("Bam Bam") => println("Bam bam!")
    case _ => println("Watch the Flintstones!")

In this case, a guard would really be needed when Person is more complex and you need to do something more than match against its parameters.

Also, as demonstrated in Recipe 4.10, instead of using this code that’s shown in the Solution:

case x if (x == 2 || x == 3) => println(x)

another possible solution is to use a variable-binding pattern:

case x @ (2|3) => println(x)

This code can be read as, “If the match expression value (i) is 2 or 3, assign that value to the variable x, then print x using println.”

4.13 Using a Match Expression Instead of isInstanceOf

Problem

You want to write a block of code to match one type, or multiple different types.

Solution

You can use the isInstanceOf method to test the type of an object:

if x.isInstanceOf[Foo] then ...

However, the “Scala way” is to prefer match expressions for this type of work, because it’s generally much more powerful and convenient to use match than isInstanceOf.

For example, in a basic use case you may be given an object of unknown type and want to determine if the object is an instance of a Person. This code shows how to write a match expression that returns true if the type is Person, and false otherwise:

def isPerson(m: Matchable): Boolean = m match
    case p: Person => true
    case _ => false

A more common scenario is that you’ll have a model like this:

enum Shape:
    case Circle(radius: Double)
    case Square(length: Double)

and then you’ll want to write a method to calculate the area of a Shape. One solution to this problem is to write area using pattern matching:

import Shape.*

def area(s: Shape): Double = s match
    case Circle(r) => Math.PI * r * r
    case Square(l) => l * l

// examples
area(Circle(2.0))   // 12.566370614359172
area(Square(2.0))   // 4.0

This is a common use, where area takes a parameter whose type is an immediate parent of the types that you deconstruct inside match.

Note that if Circle and Square took additional constructor parameters, and you only needed to access their radius and length, respectively, the complete solution looks like this:

enum Shape:
    case Circle(x0: Double, y0: Double, radius: Double)
    case Square(x0: Double, y0: Double, length: Double)

import Shape.*

def area(s: Shape): Double = s match
    case Circle(_, _, r) => Math.PI * r * r
    case Square(_, _, l) => l * l

// examples
area(Circle(0, 0, 2.0))   // 12.566370614359172
area(Square(0, 0, 2.0))   // 4.0

As shown in the case statements inside the match expression, just ignore the parameters you don’t need by referring to them with the _ character.

Discussion

As shown, a match expression lets you match multiple types, so using it to replace the isInstanceOf method is just a natural use of the match/case syntax and the general pattern-matching approach used in Scala applications.

For the most basic use cases, the isInstanceOf method can be a simpler approach to determining whether one object matches a type:

if (o.isInstanceOf[Person]) { // handle this ...

However, for anything more complex than this, a match expression is more readable than a long if/then/else if statement.

See Also

4.14 Working with a List in a Match Expression

Problem

You know that a List data structure is a little different than other sequential data structures: it’s built from cons cells and ends in a Nil element. You want to use this to your advantage when working with a match expression, such as when writing a recursive function.

Solution

You can create a List that contains the integers 1, 2, and 3 like this:

val xs = List(1, 2, 3)

or like this:

val ys = 1 :: 2 :: 3 :: Nil

As shown in the second example, a List ends with a Nil element, and you can take advantage of that when writing match expressions to work on lists, especially when writing recursive algorithms. For instance, in the following listToString method, if the current element is not Nil, the method is called recursively with the remainder of the List, but if the current element is Nil, the recursive calls are stopped and an empty String is returned, at which point the recursive calls unwind:

def listToString(list: List[String]): String = list match
    case s :: rest => s + " " + listToString(rest)
    case Nil => ""

The REPL demonstrates how this method works:

scala> val fruits = "Apples" :: "Bananas" :: "Oranges" :: Nil
fruits: List[java.lang.String] = List(Apples, Bananas, Oranges)

scala> listToString(fruits)
res0: String = "Apples Bananas Oranges "

The same approach can be used when dealing with lists of other types and different algorithms. For instance, while you could just write List(1,2,3).sum, this example shows how to write your own sum method using match and recursion:

def sum(list: List[Int]): Int = list match
    case Nil => 0
    case n :: rest => n + sum(rest)

Similarly, this is a product algorithm:

def product(list: List[Int]): Int = list match
    case Nil => 1
    case n :: rest => n * product(rest)

The REPL shows how these methods work:

scala> val nums = List(1,2,3,4,5)
nums: List[Int] = List(1, 2, 3, 4, 5)

scala> sum(nums)
res0: Int = 15

scala> product(nums)
res1: Int = 120

Don’t Forget reduce and fold

While recursion is great, Scala’s various reduce and fold methods on the collections classes are built to let you traverse a collection while applying an algorithm, and they often eliminate the need for recursion. For instance, you can write a sum algorithm using reduce in either of these two forms:

// long form
def sum(list: List[Int]): Int = list.reduce((x,y) => x + y)

// short form
def sum(list: List[Int]): Int = list.reduce(_ + _)

See Recipe 13.10, “Walking Through a Collection with the reduce and fold Methods”, for more details.

Discussion

As shown, recursion is a technique where a method calls itself in order to solve a problem. In functional programming—where all variables are immutable—recursion provides a way to iterate over the elements in a List to solve a problem, such as calculating the sum or product of all the elements in a List.

A nice thing about working with the List class in particular is that a List ends with the Nil element, so your recursive algorithms typically have this pattern:

def myTraversalMethod[A](xs: List[A]): B = xs match
    case head :: tail =>
        // do something with the head
        // pass the tail of the list back to your method, i.e.,
        // `myTraversalMethod(tail)`
    case Nil =>
        // end condition here (0 for sum, 1 for product, etc.)
        // end the traversal

Variables in Functional Programming

In FP, we use the term variables, but since we only use immutable variables, it may seem that this word doesn’t make sense, i.e., we have a variable that can’t vary.

What’s going on here is that we really mean “variable” in the algebraic sense, not in the computer programming sense. For instance, in algebra we say that a, b, and c are variables when we write this algebraic equation:

a = b * c

However, once they’re assigned, they can’t vary. The term variable has the same meaning in functional programming.

See Also

I initially found recursion to be an unnecessarily hard topic to grasp, so I’ve written quite a few blog posts about it:

4.15 Matching One or More Exceptions with try/catch

Problem

You want to catch one or more exceptions in a try/catch block.

Solution

The Scala try/catch/finally syntax is similar to Java, but it uses the match expression approach in the catch block:

try
   doSomething()
catch
   case e: SomeException => e.printStackTrace
finally
   // do your cleanup work

When you need to catch and handle multiple exceptions, just add the exception types as different case statements:

try
   openAndReadAFile(filename)
catch
   case e: FileNotFoundException =>
       println(s"Couldn’t find $filename.")
   case e: IOException =>
       println(s"Had an IOException trying to read $filename.")

You can also write that code like this, if you prefer:

try
    openAndReadAFile(filename)
catch
    case e: (FileNotFoundException | IOException) =>
        println(s"Had an IOException trying to read $filename")

Discussion

As shown, the Scala case syntax is used to match different possible exceptions. If you’re not concerned about which specific exceptions might be thrown, and want to catch them all and do something with them—such as log them—use this syntax:

try
   openAndReadAFile(filename)
catch
   case t: Throwable => logger.log(t)

If for some reason you don’t care about the value of the exception, you can also catch them all and ignore them like this:

try
   openAndReadAFile(filename)
catch
   case _: Throwable => println("Nothing to worry about, just an exception")

Methods based on try/catch

As shown in this chapter’s introduction, a try/catch/finally block can return a value and therefore be used as the body of a method. The following method returns an Option[String]. It returns a Some that contains a String if the file is found, and a None if there is a problem reading the file:

import scala.io.Source
import java.io.{FileNotFoundException, IOException}

def readFile(filename: String): Option[String] =
    try
        Some(Source.fromFile(filename).getLines.mkString)
    catch
        case _: (FileNotFoundException|IOException) => None

This shows one way to return a value from a try expression.

These days I rarely write methods that throw exceptions, but like Java, you can throw an exception from a catch clause. However, because Scala doesn’t have checked exceptions, you don’t need to specify that a method throws the exception. This is demonstrated in the following example, where the method isn’t annotated in any way:

// danger: this method doesn’t warn you that an exception can be thrown
def readFile(filename: String): String =
    try
        Source.fromFile(filename).getLines.mkString
    catch
        case t: Throwable => throw t

That’s actually a horribly dangerous method—don’t write code like this!

To declare that a method throws an exception, add the @throws annotation to your method definition:

// better: this method warns others that an exception can be thrown
@throws(classOf[NumberFormatException])
def readFile(filename: String): String =
    try
        Source.fromFile(filename).getLines.mkString
    catch
        case t: Throwable => throw t

While that last method is better than the previous one, neither one is preferred. The “Scala way” is to never throw exceptions. Instead, you should use Option, as shown previously, or use the Try/Success/Failure or Either/Right/Left classes when you want to return information about what failed. This example shows how to use Try:

import scala.io.Source
import java.io.{FileNotFoundException, IOException}
import scala.util.{Try,Success,Failure}

def readFile(filename: String): Try[String] =
    try
        Success(Source.fromFile(filename).getLines.mkString)
    catch
        case t: Throwable => Failure(t)

Whenever an exception message is involved, I always prefer using Try or Either instead of Option, because they give you access to the message in Failure or Left, where Option only returns None.

A concise way to catch everything

Another concise way to catch all exceptions is with the allCatch method of the scala.util.control.Exception object. The following examples demonstrate how to use allCatch, first showing the success case and then the failure case. The output of each expression is shown after the comment on each line:

import scala.util.control.Exception.allCatch

// OPTION
allCatch.opt("42".toInt)      // Option[Int] = Some(42)
allCatch.opt("foo".toInt)     // Option[Int] = None

// TRY
allCatch.toTry("42".toInt)    // Matchable = 42
allCatch.toTry("foo".toInt)
   // Matchable = Failure(NumberFormatException: For input string: "foo")

// EITHER
allCatch.either("42".toInt)   // Either[Throwable, Int] = Right(42)
allCatch.either("foo".toInt)
   // Either[Throwable, Int] =
   // Left(NumberFormatException: For input string: "foo")

See Also

4.16 Declaring a Variable Before Using It in a try/catch/finally Block

Problem

You want to use an object in a try block, and need to access it in the finally portion of the block, such as when you need to call a close method on an object.

Solution

In general, declare your field as an Option before the try/catch block, then bind the variable to a Some inside the try clause. This is shown in the following example, where the sourceOption field is declared before the try/catch block, and assigned inside the try clause:

import scala.io.Source
import java.io.*

var sourceOption: Option[Source] = None
try
    sourceOption = Some(Source.fromFile("/etc/passwd"))
    sourceOption.foreach { source =>
        // do whatever you need to do with 'source' here ...
        for line <- source.getLines do println(line.toUpperCase)
    }
catch
    case ioe: IOException => ioe.printStackTrace
    case fnf: FileNotFoundException => fnf.printStackTrace
finally
    sourceOption match
        case None =>
            println("bufferedSource == None")
        case Some(s) =>
            println("closing the bufferedSource ...")
            s.close

This is a contrived example—and Recipe 16.1, “Reading Text Files”, shows a much better way to read files—but it does show the approach. First, define a var field as an Option prior to the try block:

var sourceOption: Option[Source] = None

Then, inside the try clause, assign the variable to a Some value:

sourceOption = Some(Source.fromFile("/etc/passwd"))

When you have a resource to close, use a technique like the one shown (though Recipe 16.1, “Reading Text Files”, also shows a much better way to close resources). Note that if an exception is thrown in this code, sourceOption inside finally will be a None value. If no exceptions are thrown, the Some branch of the match expression will be evaluated.

Discussion

One key to this recipe is knowing the syntax for declaring Option fields that aren’t initially populated:

var in: Option[FileInputStream] = None
var out: Option[FileOutputStream] = None

This second form can also be used, but the first form is preferred:

var in = None: Option[FileInputStream]
var out = None: Option[FileOutputStream]

Don’t use null

When I first started working with Scala, the only way I could think to write this code was using null values. The following code demonstrates the approach I used in an application that checks my email accounts. The store and inbox fields in this code are declared as null fields that have the Store and Folder types (from the javax.mail package):

// (1) declare the null variables (don’t use null; this is just an example)
var store: Store = null
var inbox: Folder = null

try
    // (2) use the variables/fields in the try block
    store = session.getStore("imaps")
    inbox = getFolder(store, "INBOX")
    // rest of the code here ...
catch
    case e: NoSuchProviderException => e.printStackTrace
    case me: MessagingException => me.printStackTrace
finally
    // (3) call close() on the objects in the finally clause
    if (inbox != null) inbox.close
    if (store != null) store.close

However, working in Scala gives you a chance to forget that null values even exist, so this is not a recommended approach.

See Also

See these recipes for more details on (a) how not to use null values, and (b) how to use Option, Try, and Either instead:

Whenever you’re writing code that needs to open a resource when you start and close the resource when you finish, it can be helpful to use the scala.util.Using object. See Recipe 16.1, “Reading Text Files”, for an example of how to use this object and a much better way to read a text file.

Also, Recipe 24.8, “Handling Option Values with Higher-Order Functions”, shows other ways to work with Option values besides using a match expression.

4.17 Creating Your Own Control Structures

Problem

You want to define your own control structures to customize the Scala language, simplify your code, or create a domain-specific language (DSL).

Solution

Thanks to features like multiple parameter lists, by-name parameters, extension methods, higher-order functions, and more, you can create your own code that works just like a control structure.

For example, imagine that Scala doesn’t have its own built-in while loop, and you want to create your own custom whileTrue loop, which you can use like this:

var i = 0
whileTrue (i < 5) {
    println(i)
    i += 1
}

To create this whileTrue control structure, define a method named whileTrue that takes two parameter lists. The first parameter list handles the test condition—in this case, i < 5—and the second parameter list is the block of code the user wants to run, i.e., the code in between the curly braces. Define both parameters to be by-name parameters. Because whileTrue is only used for side effects, such as updating mutable variables or printing to the console, declare it to return Unit. An initial sketch of the method signature looks like this:

def whileTrue(testCondition: => Boolean)(codeBlock: => Unit): Unit = ???

One way to implement the body of the method is to write a recursive algorithm. This code shows a complete solution:

import scala.annotation.tailrec

object WhileTrue:
    @tailrec
    def whileTrue(testCondition: => Boolean)(codeBlock: => Unit): Unit =
        if (testCondition) then
            codeBlock
            whileTrue(testCondition)(codeBlock)
        end if
    end whileTrue

In this code, the testCondition is evaluated, and if the condition is true, codeBlock is executed, and then whileTrue is called recursively. It keeps calling itself until testCondition returns false.

To test this code, first import it:

import WhileTrue.whileTrue

Then run the whileTrue loop shown previously, and you’ll see that it works as desired.

Discussion

The creators of the Scala language made a conscious decision not to implement some keywords in Scala, and instead they implemented functionality through Scala libraries. For instance, Scala doesn’t have built-in break and continue keywords. Instead it implements them through a library, as I describe in my blog post “Scala: How to Use break and continue in for and while Loops”.

As shown in the Solution, the ability to create your own control structures comes from features like these:

  • Multiple parameter lists let you do what I did with whileTrue: create one parameter group for the test condition, and a second group for the block of code.

  • By-name parameters also let you do what I did with whileTrue: accept parameters that aren’t evaluated until they’re accessed inside your method.

Similarly, other features like infix notation, higher-order functions, extension methods, and fluent interfaces let you create other custom control structures and DSLs.

By-name parameters

By-name parameters are an important part of the whileTrue control structure. In Scala it’s important to know that when you define method parameters using the => syntax:

def whileTrue(testCondition: => Boolean)(codeBlock: => Unit) =
                           -----                  -----

you’re creating what’s known as a call-by-name or by-name parameter. A by-name parameter is only evaluated when it’s accessed inside your method, so, as I write in my blog posts “How to Use By-Name Parameters in Scala” and “Scala and Call-By-Name Parameters”, a more accurate name for these parameters is evaluate when accessed. That’s because that’s exactly how they work: they’re only evaluated when they’re accessed inside your method. As I note in that second blog post, Rob Norris makes the comparison that a by-name parameter is like receiving a def method.

Another example

In the whileTrue example, I used a recursive call to keep the loop running, but for simpler control structures you don’t need recursion. For instance, assume that you want a control structure that takes two test conditions, and if both evaluate to true, you’ll run a block of code that’s supplied. An expression using that control structure looks like this:

doubleIf(age > 18)(numAccidents == 0) { println("Discount!") }

In this case, define doubleIf as a method that takes three parameter lists, where again, each parameter is a by-name parameter:

// two 'if' condition tests
def doubleIf(test1: => Boolean)(test2: => Boolean)(codeBlock: => Unit) =
    if test1 && test2 then codeBlock

Because doubleIf only needs to perform one test and doesn’t need to loop indefinitely, there’s no need for a recursive call in its method body. It simply checks the two test conditions, and if they evaluate to true, codeBlock is executed.

See Also

Get Scala Cookbook, 2nd Edition now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.