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!"); } }
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!"); } }
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!"); } }
Expression-Oriented Programming
But where does the expression part kick in?
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!"); }
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!") }
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!") }
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);
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");
The loop terminates when the test condition (notFiften != 15
) becomes false.
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.");
In the above code, we are iterating through a range. We will see about ranges in a later article.
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.");
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;
The value of evenNumbers
will be IndexedSeq(0, 2, 4, 6... 98)
.