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.

Leave a Comment

Your email address will not be published. Required fields are marked *