Control Flow in Scala
Master conditionals, pattern matching, loops, and for-comprehensions in Scala with practical Docker-ready examples
Control flow determines the order in which your program executes its statements—which branches it takes, which blocks it repeats, and which values it produces. Scala approaches control flow with a distinctly functional philosophy: rather than treating if, match, and for as mere statements that do things, Scala treats them as expressions that return values.
This expression-oriented design is the single most important idea to internalize. In an imperative language you might write if (cond) { x = 1 } else { x = 2 }, mutating a variable. In Scala, the if itself evaluates to a value: val x = if (cond) 1 else 2. Because of this, Scala has no dedicated ternary operator—if/else already fills that role.
As a multi-paradigm language blending object-oriented and functional styles, Scala gives you familiar while loops when you need them, but it strongly favors pattern matching over switch statements and for-comprehensions over imperative iteration. In this tutorial you’ll learn conditionals, match expressions, loops, and comprehensions—and see why Scala developers reach for the functional tools first.
Conditionals: if/else as Expressions
The if/else construct works as you’d expect, but remember that it always evaluates to a value. This eliminates the need for a separate ternary operator.
Create a file named Conditionals.scala:
| |
The chained else if reads like any C-family language, but the second use is uniquely Scala: if (temperature > 20) "warm" else "cool" is an expression whose result is bound to label. The s"..." string interpolation embeds the value directly. Because both branches must produce a compatible type, the compiler infers label as a String.
Pattern Matching: Scala’s Powerful switch
Where other languages have a limited switch, Scala has match—an expression that can match on values, multiple alternatives, guards, and even types. It is the idiomatic replacement for long if/else if chains.
Create a file named Matching.scala:
| |
Several pattern-matching features appear here. The _ wildcard is the default case. The | operator matches several alternatives at once (case 6 | 7). A guard (case d if ...) adds a boolean condition to a case, binding the matched value to d. Finally, type patterns (case i: Int) let you branch on an element’s runtime type—indispensable when working with Any or sealed hierarchies. Unlike a C-style switch, there is no fall-through and no break is needed.
Loops and For-Comprehensions
Scala supports while loops and for loops, but the for construct is far more capable than its imperative cousin: with yield it becomes a for-comprehension that builds a new collection.
Create a file named Loops.scala:
| |
The 1 to 5 syntax creates an inclusive Range, while 0 until 10 is exclusive of the upper bound, and by 2 sets the step. The while loop is the one place you’ll genuinely need a mutable var—functional style otherwise avoids it. The for ... yield form is the star: it transforms a range into a new collection (here a Vector of squares) without any manual accumulation. The final block uses multiple generators plus an if guard to iterate over filtered combinations—Scala’s concise alternative to nested loops with an if inside.
Running with Docker
Run each example with the Scala CLI image. No local Scala installation required.
| |
Expected Output
Conditionals.scala produces:
It's mild outside
The weather is cool
Adjusted reading: 20
Matching.scala produces:
Day 6 is Saturday
Day 6 is a Weekend
an Int: 42
a String: hello
a Double: 3.14
something else: true
Loops.scala produces:
Counting: 1 2 3 4 5
Even numbers: 0 2 4 6 8
T-minus 3
T-minus 2
T-minus 1
Liftoff!
Squares: Vector(1, 4, 9, 16, 25)
Pairs summing to 5: (1,4) (2,3) (3,2) (4,1)
Key Concepts
- Everything is an expression —
if/elseandmatchreturn values, so you can assign their result directly to aval. This is why Scala has no separate ternary operator. - No fall-through in
match— each case is self-contained; there is nobreakand no accidental fall-through like C-styleswitch. - Pattern matching is the idiomatic branch tool — prefer
matchwith literals, alternatives (|), guards (if), and type patterns over longif/else ifchains. tovsuntil—1 to 5is inclusive (1–5);1 until 5is exclusive (1–4). Usebyto set a step size.for ... yieldbuilds collections — a for-comprehension transforms and filters data into a new collection, replacing manual accumulation loops.- Mutability is the exception —
whileloops require a mutablevar; functional Scala favors comprehensions and recursion that avoid mutation. - Guards and multiple generators — a single
forblock can combine several generators withifguards, expressing nested-and-filtered iteration concisely.
Running Today
All examples can be run using Docker:
docker pull virtuslab/scala-cli:latest
Comments
Loading comments...
Leave a Comment