03 · Control Flow¶
if/else is an expression¶
In Scala, if/else produces a value — you don't need a separate ternary
operator, and you often assign the result directly to a val:
val age = 20
val category =
if age < 13 then "child"
else if age < 20 then "teen"
else "adult"
println(category) // adult
// Old-style parentheses form still works and is common in Scala 2 code
val max =
if (10 > 5) 10
else 5
println(max) // 10
Because if/else is an expression, both branches must produce compatible
types (or Scala widens to a common supertype) for the result to be useful as
a value.
while loops¶
while exists but is used sparingly in idiomatic Scala — it's imperative
and needs a var to do anything useful:
for loops and for expressions¶
for in Scala is more powerful than a typical C-style loop — it iterates
over any collection, and with yield it becomes a for-expression that
produces a new collection (covered in depth in
Level 2):
// Simple iteration -- a "for loop" used for side effects
for i <- 1 to 5 do
println(i)
// 1
// 2
// 3
// 4
// 5
// Ranges: 1 to 5 is inclusive, 1 until 5 is exclusive
println((1 to 5).toList) // List(1, 2, 3, 4, 5)
println((1 until 5).toList) // List(1, 2, 3, 4)
// for-expression: yield produces a NEW collection instead of side effects
val doubled = for i <- 1 to 5 yield i * 2
println(doubled) // Vector(2, 4, 6, 8, 10)
// Filtering inside a for
val evens = for i <- 1 to 10 if i % 2 == 0 yield i
println(evens) // Vector(2, 4, 6, 8, 10)
// Nested iteration
val pairs = for
x <- 1 to 2
y <- 1 to 2
yield (x, y)
println(pairs) // Vector((1,1), (1,2), (2,1), (2,2))
match expressions (a preview)¶
Scala's match is like a much more powerful switch. It gets a full module
to itself (Module 8), but here's the basic
shape since it's genuinely everyday control flow:
val day = 3
val name = day match
case 1 => "Monday"
case 2 => "Tuesday"
case 3 => "Wednesday"
case _ => "Some other day"
println(name) // Wednesday
Prefer expressions over mutation¶
Idiomatic Scala favors deriving a new value over mutating an existing one. Compare two ways of summing a range:
// Imperative style -- works, but not idiomatic Scala
var total = 0
for i <- 1 to 5 do
total += i
println(total) // 15
// Functional style -- no var, sum is a method on the range
val total2 = (1 to 5).sum
println(total2) // 15
You'll see this pattern throughout the course: reach for collection methods
(sum, map, filter, fold, and friends — introduced in
Module 5 and deepened in
Level 2) before reaching for a
var and a loop.
How It Actually Works¶
if/else being an expression rather than a statement is not just style
— the compiler assigns it a type, computed as the least upper bound (the
closest common supertype) of the then and else branch types. if (x > 0)
"positive" else -1 type-checks to Any (the LUB of String and Int) —
usually a sign you meant something else, since Any erases most useful
methods. When both branches agree on a type, bytecode-wise if/else
compiles to ordinary conditional jump instructions (ifeq, ifne, goto)
just like Java's if — the "expression" nature is purely a compile-time
type-checking feature layered on top of the same JVM control-flow
primitives.
A Scala for loop over a range (for i <- 1 to 5 do ...) is not a JVM
looping construct on its own — the compiler desugars it into calls on
whatever 1 to 5 returns (a Range), specifically foreach, which
internally runs an ordinary while loop over the range's start/end/
step fields. A while loop, by contrast, compiles almost directly to
JVM bytecode's goto-based loop shape with no allocation at all — which is
why tight numeric loops in performance-sensitive Scala code sometimes still
reach for while (see Level 3's performance module)
even though for and foreach read better.
Cheat sheet¶
| Construct | Purpose |
|---|---|
if ... then ... else ... |
Conditional expression, returns a value |
while ... do ... |
Imperative loop, needs a var to be useful |
for i <- range do ... |
Loop over a collection for side effects |
for i <- range yield ... |
Build a new collection from a loop |
x match { case ... } |
Multi-way branching on a value's shape |
🔀 See this in another language¶
Exercise¶
Write a for-expression that produces a list of the squares of all odd
numbers from 1 to 20. Then write a match expression that takes an Int
grade (0-100) and returns a letter grade string ("A", "B", "C", "D",
"F") using range guards (case n if n >= 90 => "A").