Scala Control Flow: if/else, match, while, and for Loops
Scala's control flow structures are familiar to any programmer but with an important twist: most of them are expressions — they return values. This functional characteristic allows you to eliminate mutable variables and write cleaner, more composable code.
if/else as an Expression
In Scala, if/else is an expression that returns a value — not just a statement that controls execution:
val age = 25
val status = if age >= 18 then "adult" else "minor"
// status: String = "adult"Since if/else returns a value, you can use it anywhere an expression is expected — in variable assignments, function arguments, and interpolated strings:
val message = s"You are ${if age >= 18 then "an adult" else "a minor"}"
def absoluteValue(n: Int): Int =
if n >= 0 then n else -nScala 3 uses then for clarity (Scala 2 used parentheses: if (age >= 18)).
When both branches return different types, Scala infers the least upper bound:
val result = if true then 42 else "hello"
// result: Int | String = 42 (Scala 3 union type)
// or: result: Any = 42 (Scala 2)The match Expression
match is Scala's powerful pattern matching construct — far more expressive than Java's switch:
val day = "Monday"
val kind = day match
case "Saturday" | "Sunday" => "weekend"
case "Monday" => "start of week"
case _ => "weekday"match is also an expression returning a value. The _ wildcard is the default case.
Matching on types:
def describe(x: Any): String = x match
case n: Int => s"Integer: $n"
case s: String => s"String of length ${s.length}"
case true => "Boolean true"
case _ => "something else"Matching with guards:
val n = 15
val category = n match
case x if x < 0 => "negative"
case 0 => "zero"
case x if x < 10 => "small"
case _ => "large"while Loops
Scala has while for imperative iteration, though it is used sparingly in idiomatic functional code:
var i = 0
while i < 5 do
println(s"i = $i")
i += 1do/while also exists:
var x = 0
do
println(x)
x += 1
while x < 3In functional Scala, while is replaced by recursive functions or collection operations. However, while remains valid for performance-critical inner loops.
for Loops and for Comprehensions
Scala's for loop is a clean syntax over collection iteration:
for i <- 1 to 5 do
println(i)
// 1 2 3 4 5
for item <- List("a", "b", "c") do
println(item)Multiple generators (nested loops):
for
x <- 1 to 3
y <- 1 to 3
do
println(s"($x, $y)")Guards (filter with if):
for
x <- 1 to 10
if x % 2 == 0
do
println(x)
// 2 4 6 8 10yield — producing a collection:
val squares = for x <- 1 to 5 yield x * x
// squares: IndexedSeq[Int] = Vector(1, 4, 9, 16, 25)With yield, the for becomes a for comprehension — it builds and returns a new collection rather than just executing side effects. This is the functional heart of Scala's for syntax.
Ranges
Scala has concise range syntax:
1 to 5 // inclusive: 1, 2, 3, 4, 5
1 until 5 // exclusive: 1, 2, 3, 4
1 to 10 by 2 // step: 1, 3, 5, 7, 9Frequently Asked Questions
Q: When should I use match versus if/else?
Use match when you are branching on the value or type of a single expression, especially when there are three or more cases. match with sealed trait hierarchies ensures exhaustiveness (the compiler warns if you miss a case). Use if/else for simple binary conditions or when the condition involves multiple independent variables.
Q: What does case _ => mean in a match expression?
The underscore _ is the wildcard pattern — it matches any value. case _ => is the catch-all default case, equivalent to Java's default:. Without a wildcard, if no case matches, Scala throws a MatchError at runtime. Using a wildcard prevents this for non-exhaustive matches.
Q: Can a for loop return a value without yield?
A for loop without yield returns Unit — it is purely for side effects. Adding yield transforms it into a for comprehension that produces a collection. You cannot use the result of a for without yield as a value.
Part of the Scala Mastery Course — Module 4 of 22.
