Scala is a multi-paradigm language. It has Object Oriented features, Functional features and also structured programming features. This include the basic control structures you’d expect to find in a structured programming language as follows:
There is a lot to cover, take a break, grab a cofee and come back later when you are prepared for the challange. We will explain all these and some more in a single page. Are you ready yet? Ok, start scrolling down and pay attention to details!
A decision is based on a conditional expression that is also known as logical expression. You must understand propositional logic to be able to make correct conditions. Check next diagram to understand the decision workflow.
Decision Diagram
In next example we use a condition to create two logical branches. Each branch is contained into a block of code for demo purpose. Second branch will never execute since val is immutable. In real programming, x should be a variable with unknown value. Then any branch could be executed depending on condition value: true or false.
/* Demo the "if" statement */
val x: Int = 0
if (x != 0) {
// true block
println("x is not zero")
} else {
// false block
println("x is zero")
}
In next example we use "if" keyword to create a logical expression. This is not a decision statement but a new way of thinking. Making logical computations is specific to functional programming and is called expression-oriented programming (EOP). To enable this technique Scala "if" can return a response that can be captured using assignment:
// logical expressions using "if" result
val minValue = if (a < b) a else b
val maxValue = if (a > b) a else b
// sometimes there is a way to simplify
val eqValues = if (a == b) true else false
val eq2Vals = a == b //simplified expression
Note: If you are a veteran programmer, you have probably seen this before. It is called ternary operator in other languages. Ternary operator is most common in C, C++ and Java but not used in Scala.
//ternary operator in Java
int max = a >= b ? a : b;
int min = a >= b ? b : a;
Scala is using "for" keyword to create all kind of repetitive statements. First one we learn is also using Range collection, that is a fast way to generate consecutive equally spaced numbers.
Iteration Concept
Next example demonstate a simple loop without branches, controlled by variable "i" that takes value from a range of integers [1..3].
//demo: for loop with range
object Main {
def main(args: Array[String]): Unit = {
for (i <- 1 to 3) {
println(s"i= $i")
}
}
}
Scala do not have regular break and continue keywords. However it does offer similar functionality through scala.util.control.Breaks. Let's analyze next two fragments of code:
Fragment: Simulate the break statement:
//for loop with breaks
breakable {
for (i <- 1 to 10) {
println(i)
if (i > 4) break
}
}
Fragment: Simulate the continue statement:
//for loop with continue
for (j <- 1 to 10) {
breakable {
if (j < 5)
break
else
println(j)
}
}
Note: that break and breakable aren’t actually keywords; they’re methods in scala.util.control.Breaks. The "break" method is declared as follows to throw an instance of a BreakControl exception when it’s called:
//fragment: for loop with continue
private val breakException = new BreakControl
def break(): Nothing = { throw breakException }
Homework: Let's put all this together into a single example. You can open the example on-line and study. It is available at this location: scala-range
How about nested loops? For breaking nested loops is even more complicated. I will post an example on external website repl.it for anyone intrested to study this farther.
Optional: Open this example to see how labeled breakable loops are implemented in Scala: scala-labels
As previous example shows, working with break exceotion is a bit difficult in Scala. Also, exception handling add a bit of drag to your code. A more elegant solution is offered by loop filters also known as guards.
Filters can be used with a new "for" syntax that has a block to establish one or more filters. This is the preferred way of writing loops in Scala, to separate the control from the executable block.
//for loop with filter
for {
control_var <- range_or_source
if filter_conditional1(control_var)
if filter_conditional2(control_var)
....
if filter_conditionalN(control_var)
} doSomething(file)
This example show the old style for loop with one filter condition:
//for loop with filter
for (i <- 1 to 10 if i % 2 == 0) println(i)
This example show the new style for the same loop (more readable):
//for loop with filter
for {
i <- 1 to 10
if i % 2 == 0
} println(i)
Scala has a concept of a match expression to make for a selection statement that we have in most structured programming languages. In the most simple case you can use a match expressionlike a switch statement.
Match Selector
In next example we demo pattern matching using a simple loop and random number generator: scala.util.Random to generate 10 random touples.
/* demo for pattern matching */
import scala.util.Random
object Main {
def main(args: Array[String]): Unit = {
for (i <- 1 to 10) {
val x: Int = Random.nextInt(10) // using match expression
val y = x match {
case 0 => "zero"
case 1 => "one"
case 2 => "two"
case 3 => "three"
case _ => "other"
}
println(s"($x, $y)")
} //end for
} //end main
}
Every time you run previous example you get a different result. Here is my result:
(4, other) (0, zero) (8, other) (4, other) (0, zero) (3, three) (0, zero) (8, other) (8, other) (9, other)
Scala has a try/catch/finally structure like Java. Scala uses the same syntax as the "match expressions" you have just learned above. You can use "case" statements to match different possible exceptions in the catch section of "try" statement.
This statement comes as a complex block with 3 main blocks. The "try" block is the block of code we will protect against errors, the "catch" block is the block for catching different errors. The last block is the "finally" block that executes in any event before control is released to next statement.
//syntax pseudocode
try {
// statements that can throw exceptions
...
}
catch {
case foo: FooException => handleFooException(foo)
case bar: BarException => handleBarException(bar)
...
case _: Throwable => println("other exception")
} finally {
/* unblock some resources:
closing a database connection
closing a a file */
...
}
More later: I will cover more details about try/catch/finally syntax in our next page. I need to learn more about functional programming and exceptions before I can continue. Thanks for reading so far.
Read next: Packages