Scala 3 — Book

Control Structures

Language

Scala has the control structures you find in other programming languages, and also has powerful for expressions and match expressions:

  • if/else
  • for loops and expressions
  • match expressions
  • while loops
  • try/catch

These structures are demonstrated in the following examples.

if/else

Scala’s if/else control structure looks similar to other languages.

if (x < 0) {
  println("negative")
} else if (x == 0) {
  println("zero")
} else {
  println("positive")
}
if x < 0 then
  println("negative")
else if x == 0 then
  println("zero")
else
  println("positive")

Note that this really is an expression—not a statement. This means that it returns a value, so you can assign the result to a variable:

val x = if (a < b) { a } else { b }
val x = if a < b then a else b

As you’ll see throughout this book, all Scala control structures can be used as expressions.

An expression returns a result, while a statement does not. Statements are typically used for their side-effects, such as using println to print to the console.

for loops and expressions

The for keyword is used to create a for loop. This example shows how to print every element in a List:

val ints = List(1, 2, 3, 4, 5)

for (i <- ints) println(i)

The code i <- ints is referred to as a generator. In any generator p <- e, the expression e can generate zero or many bindings to the pattern p. The code that follows the closing parentheses of the generator is the body of the loop.

val ints = List(1, 2, 3, 4, 5)

for i <- ints do println(i)

The code i <- ints is referred to as a generator, and the code that follows the do keyword is the body of the loop.

Guards

You can also use one or more if expressions inside a for loop. These are referred to as guards. This example prints all of the numbers in ints that are greater than 2:

for (i <- ints if i > 2)
  println(i)
for
  i <- ints
  if i > 2
do
  println(i)

You can use multiple generators and guards. This loop iterates over the numbers 1 to 3, and for each number it also iterates over the characters a to c. However, it also has two guards, so the only time the print statement is called is when i has the value 2 and j is the character b:

for {
  i <- 1 to 3
  j <- 'a' to 'c'
  if i == 2
  if j == 'b'
} {
  println(s"i = $i, j = $j")   // prints: "i = 2, j = b"
}
for
  i <- 1 to 3
  j <- 'a' to 'c'
  if i == 2
  if j == 'b'
do
  println(s"i = $i, j = $j")   // prints: "i = 2, j = b"

for expressions

The for keyword has even more power: When you use the yield keyword instead of do, you create for expressions which are used to calculate and yield results.

A few examples demonstrate this. Using the same ints list as the previous example, this code creates a new list, where the value of each element in the new list is twice the value of the elements in the original list:

scala> val doubles = for (i <- ints) yield i * 2
val doubles: List[Int] = List(2, 4, 6, 8, 10)
scala> val doubles = for i <- ints yield i * 2
val doubles: List[Int] = List(2, 4, 6, 8, 10)

Scala’s control structure syntax is flexible, and that for expression can be written in several other ways, depending on your preference:

val doubles = for (i <- ints) yield i * 2
val doubles = for (i <- ints) yield (i * 2)
val doubles = for { i <- ints } yield (i * 2)
val doubles = for i <- ints yield i * 2     // style shown above
val doubles = for (i <- ints) yield i * 2
val doubles = for (i <- ints) yield (i * 2)
val doubles = for { i <- ints } yield (i * 2)

This example shows how to capitalize the first character in each string in the list:

val names = List("chris", "ed", "maurice")
val capNames = for (name <- names) yield name.capitalize
val names = List("chris", "ed", "maurice")
val capNames = for name <- names yield name.capitalize

Finally, this for expression iterates over a list of strings, and returns the length of each string, but only if that length is greater than 4:

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

val fruitLengths =
  for (f <- fruits if f.length > 4) yield f.length

// fruitLengths: List[Int] = List(5, 6, 6)
val fruits = List("apple", "banana", "lime", "orange")

val fruitLengths = for
  f <- fruits
  if f.length > 4
yield
  // you can use multiple lines
  // of code here
  f.length

// fruitLengths: List[Int] = List(5, 6, 6)

for loops and expressions are covered in more detail in the Control Structures sections of this book, and in the Reference documentation.

match expressions

Scala has a match expression, which in its most basic use is like a Java switch statement:

val i = 1

// later in the code ...
i match {
  case 1 => println("one")
  case 2 => println("two")
  case _ => println("other")
}
val i = 1

// later in the code ...
i match
  case 1 => println("one")
  case 2 => println("two")
  case _ => println("other")

However, match really is an expression, meaning that it returns a result based on the pattern match, which you can bind to a variable:

val result = i match {
  case 1 => "one"
  case 2 => "two"
  case _ => "other"
}
val result = i match
  case 1 => "one"
  case 2 => "two"
  case _ => "other"

match isn’t limited to working with just integer values, it can be used with any data type:

val p = Person("Fred")

// later in the code
p match {
  case Person(name) if name == "Fred" =>
    println(s"$name says, Yubba dubba doo")

  case Person(name) if name == "Bam Bam" =>
    println(s"$name says, Bam bam!")

  case _ => println("Watch the Flintstones!")
}
val p = Person("Fred")

// later in the code
p match
  case Person(name) if name == "Fred" =>
    println(s"$name says, Yubba dubba doo")

  case Person(name) if name == "Bam Bam" =>
    println(s"$name says, Bam bam!")

  case _ => println("Watch the Flintstones!")

In fact, a match expression can be used to test a variable against many different types of patterns. This example shows (a) how to use a match expression as the body of a method, and (b) how to match all the different types shown:

// getClassAsString is a method that takes a single argument of any type.
def getClassAsString(x: Any): String = x match {
  case s: String => s"'$s' is a String"
  case i: Int => "Int"
  case d: Double => "Double"
  case l: List[_] => "List"
  case _ => "Unknown"
}

// examples
getClassAsString(1)               // Int
getClassAsString("hello")         // 'hello' is a String
getClassAsString(List(1, 2, 3))   // List

Because the method getClassAsString takes a parameter value of type Any, it can be decomposed by any kind of pattern.

// getClassAsString is a method that takes a single argument of any type.
def getClassAsString(x: Matchable): String = x match
  case s: String => s"'$s' is a String"
  case i: Int => "Int"
  case d: Double => "Double"
  case l: List[?] => "List"
  case _ => "Unknown"

// examples
getClassAsString(1)               // Int
getClassAsString("hello")         // 'hello' is a String
getClassAsString(List(1, 2, 3))   // List

The method getClassAsString takes as a parameter a value of type Matchable, which can be any type supporting pattern matching (some types don’t support pattern matching because this could break encapsulation).

There’s much more to pattern matching in Scala. Patterns can be nested, results of patterns can be bound, and pattern matching can even be user-defined. See the pattern matching examples in the Control Structures chapter for more details.

try/catch/finally

Scala’s try/catch/finally control structure lets you catch exceptions. It’s similar to Java, but its syntax is consistent with match expressions:

try {
  writeTextToFile(text)
} catch {
  case ioe: IOException => println("Got an IOException.")
  case nfe: NumberFormatException => println("Got a NumberFormatException.")
} finally {
  println("Clean up your resources here.")
}
try
  writeTextToFile(text)
catch
  case ioe: IOException => println("Got an IOException.")
  case nfe: NumberFormatException => println("Got a NumberFormatException.")
finally
  println("Clean up your resources here.")

while loops

Scala also has a while loop construct. Its one-line syntax looks like this:

while (x >= 0) { x = f(x) }
while x >= 0 do x = f(x)

Scala 3 still supports the Scala 2 syntax for the sake of compatibility.

The while loop multiline syntax looks like this:

var x = 1

while (x < 3) {
  println(x)
  x += 1
}
var x = 1

while
  x < 3
do
  println(x)
  x += 1

Custom control structures

Thanks to features like by-name parameters, infix notation, fluent interfaces, optional parentheses, extension methods, and higher-order functions, you can also create your own code that works just like a control structure. You’ll learn more about this in the Control Structures section.

Contributors to this page: