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 andfor
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
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
-
For more examples of how to use
zipWithIndex
, see Recipe 13.4, “Using zipWithIndex or zip to Create Loop Counters”. -
For more examples of how to iterate over the elements in a
Map
, see Recipe 14.9, “Traversing a Map”.
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
(
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:
-
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 aVector
, the output type will also be aVector
. You can think of this new collection as being like an empty bucket. -
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. -
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
-
Comparisons between
for
comprehensions andmap
are shown in more detail in Recipe 13.5, “Transforming One Collection to Another with map”.
4.5 Using the if Construct Like a Ternary Operator
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
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
-
For more information on how JVM switches work, see the JVM spec on compiling switches.
-
Regarding the difference between a
lookupswitch
andtableswitch
, this Stack Overflow page states, “The difference is that a lookupswitch uses a table with keys and labels, yet a tableswitch uses a table with labels only.” Again, see the “Compiling Switches” section of the Java Virtual Machine (JVM) specification for more details. -
See Recipe 10.7, “Creating Partial Functions”, for more information on partial functions.
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
-
See Recipe 4.12 for a related approach.
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
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 anInt
value of0
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 thecase
statement. For example, at the end of amatch
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, andstr
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 tovariableName
.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 is1
, but so far, theList
hasn’t been accessed on the right side of the expression. When accessing aList
, 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 thematch
expression, the commented-out line of code won’t compile, but the second line shows how to match the desiredList
object and then bind that list to the variablex
. When this line of code matches a list likeList(1,2,3)
, it results in the outputList(1, 2, 3)
, as shown in the output of the firstprintln
statement.The first
Some
example shows that you can match aSome
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 theSome
, and the third example takes this a step further, giving you access to theSome
object itself. When it’s matched by the secondprintln
call, it printsSome(foo)
, demonstrating that you now have access to theSome
object.Finally, this approach is used to match a
Person
whose last name isDoe
. This syntax lets you assign the result of the pattern match to the variablep
, 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
-
A discussion of getting around type erasure when using
match
expressions on Stack Overflow
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
-
Recipe 4.10 shows many more
match
techniques.
4.14 Working with a List in a Match Expression
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:
-
In “Recursion: Thinking Recursively”, I write about identity elements, including how
0
is an identity element for the sum operation,1
is an identity element for the product operation, and""
(a blank string) is an identity element for working with strings.
4.15 Matching One or More Exceptions with try/catch
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
-
See Recipe 8.7, “Declaring That a Method Can Throw an Exception”, for more examples of declaring that a method can throw an exception.
-
See Recipe 24.6, “Using Scala’s Error-Handling Types (Option, Try, and Either)”, for more information on using
Option
/Some
/None
andTry
/Success
/Failure
. -
See the
scala.util.control.Exception
Scaladoc page for moreallCatch
information.
4.16 Declaring a Variable Before Using It in a try/catch/finally Block
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:
-
Recipe 24.6, “Using Scala’s Error-Handling Types (Option, Try, and Either)”
-
Recipe 24.8, “Handling Option Values with Higher-Order Functions”
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
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
-
One of my favorite uses of this technique is shown in the book Beginning Scala by David Pollak (Apress). Although it’s rendered obsolete by the
scala.util.Using
object, I describe how the technique works in this blog post, “The using Control Structure in Beginning Scala”. -
The Scala
Breaks
class is used to implement break and continue functionality infor
loops, and I wrote about it: “Scala: How to Use break and continue in for and while Loops”. TheBreaks
class source code is fairly simple and provides another example of how to implement a control structure. You can find its source code as a link on its Scaladoc page.
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.