Scala Pattern Matching Basics
Pattern matching is one of Scala's most powerful features. It compares a value against a series of patterns and runs the code for the first match. Unlike a simple switch statement, Scala's match works on values, types, case classes, collections, guards, and nested structures — all in one clean syntax.
Basic Syntax
val x = 3
val label = x match
case 1 => "one"
case 2 => "two"
case 3 => "three"
case _ => "something else" // wildcard — matches anything
println(label) // three
x = 3
│
├── case 1? No
├── case 2? No
├── case 3? Yes → "three"
└── (stops here, does not check _)
match Returns a Value
val score = 78
val grade = score match
case s if s >= 90 => "A"
case s if s >= 80 => "B"
case s if s >= 70 => "C"
case s if s >= 60 => "D"
case _ => "F"
println(s"Grade: $grade") // Grade: C
Matching Literals
def describe(day: String): String = day match
case "Monday" => "Start of the week"
case "Friday" => "Almost weekend"
case "Saturday" | "Sunday" => "Weekend!"
case d if d.startsWith("T") => s"$d starts with T"
case _ => "A weekday"
println(describe("Monday")) // Start of the week
println(describe("Saturday")) // Weekend!
println(describe("Tuesday")) // Tuesday starts with T
println(describe("Wednesday")) // A weekday
Matching Types
def identify(value: Any): String = value match
case i: Int => s"Integer: $i"
case d: Double => f"Double: $d%.2f"
case s: String => s"String '$s' (length ${s.length})"
case b: Boolean => s"Boolean: $b"
case l: List[_] => s"List with ${l.length} elements"
case _ => "Unknown type"
println(identify(42)) // Integer: 42
println(identify(3.14)) // Double: 3.14
println(identify("Scala")) // String 'Scala' (length 5)
println(identify(List(1, 2, 3))) // List with 3 elements
Matching Case Classes
case class Point(x: Int, y: Int)
case class Circle(center: Point, radius: Double)
def describeShape(shape: Any): String = shape match
case Point(0, 0) => "Origin"
case Point(x, 0) => s"On X-axis at $x"
case Point(0, y) => s"On Y-axis at $y"
case Point(x, y) => s"Point ($x, $y)"
case Circle(Point(0,0), r) => f"Circle at origin, r=$r%.1f"
case Circle(c, r) => f"Circle at ${describeShape(c)}, r=$r%.1f"
println(describeShape(Point(0, 0))) // Origin
println(describeShape(Point(3, 0))) // On X-axis at 3
println(describeShape(Circle(Point(0,0), 5.0))) // Circle at origin, r=5.0
println(describeShape(Circle(Point(2,3), 1.5))) // Circle at Point (2, 3), r=1.5
Binding a Value with @
Use @ to give a name to the whole matched value while still pattern-matching its structure:
case class Person(name: String, age: Int)
def greet(p: Person): String = p match
case person @ Person(_, age) if age < 18 =>
s"Hi ${person.name}, you're a minor ($age)"
case person @ Person(name, _) =>
s"Hello, $name! Your record: $person"
println(greet(Person("Ali", 15)))
// Hi Ali, you're a minor (15)
println(greet(Person("Sara", 30)))
// Hello, Sara! Your record: Person(Sara,30)
Matching Tuples
def quadrant(point: (Int, Int)): String = point match
case (0, 0) => "Origin"
case (x, 0) if x > 0 => s"Positive X-axis"
case (0, y) if y > 0 => s"Positive Y-axis"
case (x, y) if x > 0 && y > 0 => "Quadrant I"
case (x, y) if x < 0 && y > 0 => "Quadrant II"
case (x, y) if x < 0 && y < 0 => "Quadrant III"
case _ => "Quadrant IV"
println(quadrant((3, 5))) // Quadrant I
println(quadrant((-2, 4))) // Quadrant II
println(quadrant((0, 0))) // Origin
Matching Lists
def describeList(list: List[Int]): String = list match
case Nil => "empty"
case x :: Nil => s"single element: $x"
case x :: y :: Nil => s"two elements: $x and $y"
case x :: y :: rest => s"starts with $x, $y, and ${rest.length} more"
println(describeList(List())) // empty
println(describeList(List(42))) // single element: 42
println(describeList(List(1, 2))) // two elements: 1 and 2
println(describeList(List(1,2,3,4))) // starts with 1, 2, and 2 more
Guards in Patterns
val numbers = List(15, -3, 42, 0, -17, 8, 100)
numbers.foreach { n =>
val label = n match
case 0 => "zero"
case n if n < 0 => s"negative ($n)"
case n if n > 50 => s"large ($n)"
case n => s"normal ($n)"
println(label)
}
// large (15) ... wait: 15 is not > 50, so:
// normal (15), negative (-3), normal (42), zero, negative (-17), normal (8), large (100)
Exhaustiveness Warning
sealed trait Color
case object Red extends Color
case object Green extends Color
case object Blue extends Color
// The compiler warns if any case is missing
def hex(c: Color): String = c match
case Red => "#FF0000"
case Green => "#00FF00"
case Blue => "#0000FF"
// Remove any case above and the compiler gives a warning
Sealed traits and exhaustive match expressions work together to make your code provably complete. The compiler acts as a safety net, catching missing cases before your program runs.
