Control Structures

Blocks

A block is a sequence of semi-colon-separated declarations and expressions surrounded by curly braces:

scala> {val x = 10; def square(x: Double) = x * x; square(x + 3)}
res23: Double = 169.0

The scope of all declarations inside of a block extends to the end of the block. In the example above square and x disappear as soon as the block terminates:

scala> square(x)
<console>:8: error: not found: value square
              square(x)
              ^
<console>:8: error: not found: value x
              square(x)

                     ^

A block is an expression. Its value is the value of its last sub-expression:

scala> {1; 2; 3; 4; 5}
res25: Int = 5

Conditional Expressions

Scala conditional expressions are similar to Java's x?y:z expression:

scala> def max(x: Int, y: Int) = if (x > y) x else y
max: (x: Int, y: Int)Int

scala> max(2, 3)
res26: Int = 3

Scala's Match

Scala's match is a powerful improvement to switch.

Format:

key match {
   case guard1 => branch1
   case guard2 => branch2
   case guard3 => branch3
   // etc
   case _ => default
}

Matching values:

var day = 3
day match {
   case 0 => println("Sunday")
   case 1 => println("Monday")
   case 2 => println("Tueaday")
   case 3 => println("Wednesday")
   case 4 => println("Thursday")
   case 5 => println("Friday")
   case 6 => println("Saturday")
   case _ => throw new Exception("Invalid day")
}

day match {
   case 0 | 1 => println("Weekend")
   case 2 | 3 | 4 | 5 => println("Weekday")
}

Matching types:

var shape: Shape = new Triangle(20, 10)
shape match {
   case shape: Triangle => println(shape.area)
   case shape: Rectangle => println(shape.area)
   case _ => throw new Exception("Invalid shape")
}

 

try { ... }
catch {
   case e: NegativeAmountException => println("amount must be positive")
   case e: InsufficientFundsException => println("Insufficient funds")
   case e: BadCommandException => println("Invalid command, type help")
   case _: Throwable => println("unknown error")
}

Matching Conditions

income match {
   case income if income < 100 => .1 * income
   case income if income < 1000 => .2 * income
   case income if income < 10000 => .3 * income
   case _ => .1 * income
}

Matching patterns

We can use a regular expression to extract matching elements from a string. (More on this later.)

val expPattern = """([0-9]+)\s*(\+|\*|-|/)\s*([0-9]+)""".r

def eval(expString) =  
    
exp match {
       
case expPattern(arg1"+"arg2) => arg1.toInt + arg2.toInt
       
case expPattern(arg1"-"arg2) => arg1.toInt - arg2.toInt
       
case expPattern(arg1"*"arg2) => arg1.toInt * arg2.toInt
       
case expPattern(arg1"/"arg2) => arg1.toInt / arg2.toInt
       
case _ => throw new Exception("Invalid expression")
    }

 

Iterative Expressions

Scala has the same while and do loops as Java:

while (condition) expression

do expression while (condition)

The for loop is like Java's enhanced for loop:

for(i <- init to end) expression // while(i <= end) ...
for(i <- init until end) expression // while(i < end) ...

Here are a few examples:

scala> for (i <- 0 to 10) println(i)
0
1
2
3
4
5
6
7
8
9
10

scala> for(i <- 0 to 3; j <- 0 to 3) println(i + j)
0
1
2
3
1
2
3
4
2
3
4
5
3
4
5
6

scala> val greeting = "bon jour"
greeting: String = bon jour
scala> for(i <- 0 until greeting.length) println(greeting(i))
b
o
n
 
j
o
u
r

 

scala> for(c <- greeting) println(c)

b

o

n

 

j

o

u

r

 

 

The for loop can have multiple generators and guards:

scala> for(i <- 0 to 15 if i % 3 == 0) println(i)

0

3

6

9

12

15

 

scala> for(i <- 0 until 5; j <- 0 until 5 if i != j) { println("" + i + " + " + j + " = " + (i + j)); }
0 + 1 = 1
0 + 2 = 2
0 + 3 = 3
0 + 4 = 4
1 + 0 = 1
1 + 2 = 3
1 + 3 = 4
1 + 4 = 5
2 + 0 = 2
2 + 1 = 3
2 + 3 = 5
2 + 4 = 6
3 + 0 = 3
3 + 1 = 4
3 + 2 = 5
3 + 4 = 7
4 + 0 = 4
4 + 1 = 5
4 + 2 = 6
4 + 3 = 7

A comprehension yields a collection:

scala> for(i <- 2 until 10) yield 3 * i + 2
res3: scala.collection.immutable.IndexedSeq[Int] = Vector(8, 11, 14, 17, 20, 23, 26, 29)

Arrays

Defining and traversing fixed-length arrays:

val days = Array("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday")

for(i <- 0 until days.length) {
     println(days(i))
}

for(day <- days) {
  println(day)
}

val squares = new Array[Int](10)
              
/
for(i <- 0 until 10) {
   squares(i) = i * i
}

Exceptions

Scala functions can throw exceptions just like Java methods. One difference is that the types of exceptions thrown do not need to be declared.

In Scala we use a try/catch/finally block to handle exceptions. One difference with Java is that we can use pattern-driven control in a single catch clause to determine the type of exception caught and how to handle it.

We'll see an example of this in the ATM project that follows.

What's Missing?

break

continue

goto

Do we need 'em?

What's Superfluous?

return

most semi-colons

specifying types other than parameter and return types of recursive functions.

empty parentheses when calling a parameterless function

Break Blocks

Although break and continue aren't in the Scala, a more general form of break can be imported from the scala library:

import scala.util.control.Breaks._

object TestBreak {
   def main(args: Array[String]): Unit = {
     println("entering main")
     breakable {
       println("A")
       breakable {
         println("B")
         break
         println("C")
       }
       println("D")
       break
       println("E")
     }
     println("exiting main")
   }
}

Output:

entering main
A
B
D
exiting main