Control Structures in Scala

Scala provides a bunch of control structures for controlling the flow of the program.

  • if/else expressions (not statements).
  • match expressions.
  • while loops.
  • for expressions (again, not just looping statements).
  • try/catch expressions.

What is the difference between an expression and a statement?

An expression is something that returns a result. eg. 1 + 2. This returns an actual result, 3.

A statement, however, need not return a result. eg. c = 1 + 2. This simply assigns the result of the expression to c and does not return any result.

The if/else Construct

Here we have our traditional selection statement, if, but as an expression. In other words, the if statement always returns a result.

Plain if Construct

The if construct can be used to run a block depending on a condition.

 object ControlStructures {   def main(args: Array[String]) = {     val onePiece = true;     if (onePiece == true) {        println("The One Piece is real!");     } }
 @main def ControlStructures() =    val onePiece = true;   if onePiece == true then      println("The One Piece is real!");

The if/else Construct

if blocks can be accompanied with a fallback else block.

 object ControlStructures {   def main(args: Array[String]) = {     val onePiece = true;     if (onePiece == true) {        println("The One Piece is real!");     } else {       println("The One Piece is not real!");     } }
 @main def ControlStructures() =    val onePiece = true;   if onePiece == true then      println("The One Piece is real!");   else     println("The One Piece is not real!");

The else-if Ladder

else blocks can contain another if block as a lone child. This can create a ladder of if-else-if expressions.

 object ControlStructures {   def main(args: Array[String]) = {     val onePiece = 1;     if (onePiece == 1) {        println("The One Piece is real!");     } else if (onePiece == 0) {       println("The One Piece is not real!");     } else {       println("The One Piece is a mystery!");     } }
 @main def ControlStructures() =    val onePiece = 1;   if onePiece == 1 then     println("The One Piece is real!");   else if onePiece == 0 then     println("The One Piece is not real!");   else     println("The One Piece is a mystery!");

Expression-Oriented Programming

But where does the expression part kick in?

Exclusive usage of expressions in programming is called Expression-Oriented Programming.

If you have used C or JavaScript, you would be familiar with the concept of ternaries, expressions that return a result based on a condition. If you have used Python or Rust, Scala's if expression would be an easy thing.

Let us rewrite our if/else code to utilise this nature of the if construct.

 object ControlStructures {   def main(args: Array[String]) = {     val onePiece = true;     println(s"The One Piece is ${if (onePiece == true) "" else "not "}real!"); }
 @main def ControlStructures() =    val onePiece = true;    println(s"The One Piece is ${if (onePiece == true) then "" else "not "}real!");

if (onePiece == true) "" else "not " adds a "not " depending on whether onePiece is false.

This prints "The One Piece is real!".

match Expressions

match is similar to switch in languages like C, Java, and JavaScript. However, match is actually an expression, unlike switch, a statement.

Let us rewrite our else-if ladder code to use a match construct.

 val onePiece = 1; onePiece match {   case 1 => println("The One Piece is real!")   case 0 => println("The One Piece is not real!")   case _ => println("The One Piece is a mystery!") }
 val onePiece = 1; onePiece match   case 1 => println("The One Piece is real!")   case 0 => println("The One Piece is not real!")   case _ => println("The One Piece is a mystery!")

You can also make it match a certain pattern, like a type.

 val onePiece = "1"; onePiece match {   case op: Int => println("The One Piece is an integer!")   case op: String => println("The One Piece is a string!")   case _ => println("The One Piece is a mystery!") }
 val onePiece = 1; onePiece match   case op: Int => println("The One Piece is an integer!")   case op: String => println("The One Piece is a string!")   case _ => println("The One Piece is a mystery!")

And as always, match is an expression, so you can assign the result of a match expression to a variable.

 val onePiece = 1; val result = onePiece match {   case 1 => "The One Piece is real!"   case 0 => "The One Piece is not real!"   case _ => "The One Piece is a mystery!" }; println(result);
 val onePiece = 1; val result = onePiece match   case 1 => "The One Piece is real!"   case 0 => "The One Piece is not real!"   case _ => "The One Piece is a mystery!" println(result);

match expressions can also be used with case classes. We will see them in detail in a later article.

The while Loop

The while loop is a loop that runs a block of code as long as a given condition remains valid.

 var notFifteen = 1; while(notFifteen != 15) {   notFifteen = notFifteen + 1; } println("notFifteen is now fifteen");
 var notFifteen = 1; while    notFifteen != 15  do    notFifteen = notFifteen + 1; println("notFifteen is now fifteen");

The loop terminates when the test condition (notFiften != 15) becomes false.

The do {} while () loop exists in Scala 2. However, it is deprecated and should be avoided.

The for Construct

for loops have been a widely-used, yet highly redundant feature in languages like C where they seemed to be no more than a while loop with less boilerplate.

for loops in Scala are actually expressions and they follow the function of these loops in languages like Rust and Python.

They are used to iterate over collections. In this article, we shall use them to iterate over ranges of integers.

The for Loop

 println("Counting..."); for (n <- 0 until 100) {   println(n); } println("The end.");
 println("Counting..."); for n <- 0 until 100 do   println(n); println("The end.");

In the above code, we are iterating through a range. We will see about ranges in a later article.

You can create an inclusive loop using to instead of until.
val n = 0 until 5; // 0, 1, 2, 3, 4val m = 0 to 5; // 0, 1, 2, 3, 4, 5

You can also add a condition to make it skip some iterations. For example, the below code prints all EVEN numbers from 0 until 100.

 println("Counting..."); // print only if n % 2 is 0 for (n <- 0 until 100 if n % 2 == 0) {   println(n); } println("The end.");
 println("Counting..."); // print only if n % 2 is 0 for n <- 0 until 100 if n % 2 == 0 do   println(n); println("The end.");

These conditions are called guards.

for Loops As Expressions

You can use for loops as expressions by using the yield keyword.

The yield keyword collects the result of each iteration and returns an Indexed Sequence of results.

 val evenNumbers = for (n <- 0 until 100 if n % 2 == 0) yield n;
 val evenNumbers = for n <- 0 until 100 if n % 2 == 0 yield n;

The value of evenNumbers will be IndexedSeq(0, 2, 4, 6... 98).