Pattern Matching
## Learning Objectives
- Master match expressions
- Learn pattern types
- Use guards in patterns
- Work with case objects
- Understand extractors
## Basic match Expression
### Syntax
```scala
val x = 10
val result = x match {
case 1 => "one"
case 2 => "two"
case 3 => "three"
case _ => "something else"
}
```
### match as Expression
```scala
def describe(x: Any): String = x match {
case 1 => "one"
case "hello" => "a greeting"
case true => "truth"
case List(1, 2, 3) => "list of three numbers"
case _ => "unknown"
}
```
## Pattern Types
### Constant Patterns
```scala
def describe(x: Any): String = x match {
case 0 => "zero"
case true => "true"
case "hello" => "greeting"
case '\n' => "newline"
case _ => "other"
}
```
### Variable Patterns
```scala
def describe(x: Any): String = x match {
case 0 => "zero"
case something => s"value is $something"
}
```
### Wildcard Pattern
```scala
x match {
case 1 => "one"
case _ => "not one"
}
```
### Type Patterns
```scala
def describe(x: Any): String = x match {
case i: Int => s"Integer: $i"
case s: String => s"String: $s"
case l: List[_] => s"List with ${l.length} elements"
case _: BigInt => "big integer"
case null => "null"
case _ => "something else"
}
```
### List Patterns
```scala
def describeList(lst: List[Int]): String = lst match {
case List() => "empty list"
case List(x) => s"single element: $x"
case List(x, y) => s"two elements: $x and $y"
case List(x, _*) => s"first element: $x, and more"
case _ => "not a list"
}
// Cons patterns
def sum(lst: List[Int]): Int = lst match {
case head :: tail => head + sum(tail)
case Nil => 0
}
```
### Tuple Patterns
```scala
def describeTuple(t: (Any, Any)): String = t match {
case (x: Int, y: Int) => s"int pair: ($x, $y)"
case (x: String, _) => s"string first: $x"
case (_, _) => "any 2-tuple"
}
// Destructuring in patterns
def add(tuple: (Int, Int, Int)) = tuple match {
case (a, b, c) => a + b + c
}
```
### Case Class Patterns
```scala
case class Person(name: String, age: Int)
def describe(p: Person): String = p match {
case Person("Alice", 30) => "Alice is 30"
case Person(name, age) => s"$name is $age years old"
}
val alice = Person("Alice", 30)
alice match {
case Person(n, a) => println(s"$n, $a")
}
```
### Sealed Class Patterns
```scala
sealed trait Result
case class Success(data: Int) extends Result
case class Failure(message: String) extends Result
case object Pending extends Result
def describe(result: Result): String = result match {
case Success(n) => s"Success with $n"
case Failure(msg) => s"Failed: $msg"
case Pending => "Still pending"
}
```
## Guards
### Adding Conditions
```scala
def describe(x: Int): String = x match {
case i if i > 0 => "positive"
case i if i < 0 => "negative"
case 0 => "zero"
}
def grade(score: Int): Char = 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'
}
```
### Combining Patterns and Guards
```scala
def describeList(lst: List[Int]): String = lst match {
case List(1, _*) if lst.length >= 3 => "starts with 1, has 3+ elements"
case List(x, y, z) if x + y == z => s"$x + $y = $z"
case _ => "other"
}
```
## Case Objects
```scala
sealed trait Status
case object Ready extends Status
case object Running extends Status
case object Done extends Status
case object Failed extends Status
def describe(status: Status): String = status match {
case Ready => "Ready to start"
case Running => "Currently running"
case Done => "Completed successfully"
case Failed => "Failed"
}
```
## Extractors
### unapply Method
```scala
class Person(val name: String, val age: Int)
object Person {
def apply(name: String, age: Int): Person = new Person(name, age)
def unapply(p: Person): Option[(String, Int)] = Some((p.name, p.age))
}
val p = Person("Alice", 30)
p match {
case Person(name, age) => s"$name is $age"
}
// Can also work with regular tuple
val tuple = ("Bob", 25)
tuple match {
case (name, age) => s"$name is $age"
}
```
### Boolean Extractors
```scala
object Even {
def unapply(n: Int): Boolean = n % 2 == 0
}
42 match {
case Even() => "even"
case _ => "odd"
}
```
### Custom Extractors
```scala
object Email {
def unapply(str: String): Option[(String, String)] = {
val parts = str.split("@")
if (parts.length == 2) Some(parts(0), parts(1))
else None
}
}
"user@example.com" match {
case Email(user, domain) => s"User: $user, Domain: $domain"
case _ => "Not an email"
}
```
## Pattern Matching in Assignments
```scala
val (x, y) = (1, 2)
val List(first, second, _*) = List(1, 2, 3, 4, 5)
val Person(n, a) = Person("Alice", 30)
```
## Pattern Matching in for Loops
```scala
val people = List(("Alice", 30), ("Bob", 25), ("Charlie", 35))
for ((name, age) <- people if age >= 30) {
println(s"$name is $age")
}
// With pattern matching
people foreach {
case (name, age) if age >= 30 => println(s"$name is $age")
case (name, _) => println(s"$name is too young")
}
```
## Regex Patterns
```scala
val emailRegex = "([a-z]+)@([a-z]+)\\.([a-z]+)".r
"user@example.com" match {
case emailRegex(user, domain, tld) =>
s"User: $user, Domain: $domain, TLD: $tld"
case _ => "Invalid email"
}
// Extraction
val emailRegex(user, domain, tld) = "user@example.com"
// user = "user", domain = "example", tld = "com"
```
## Partial Functions
```scala
val divide: PartialFunction[(Int, Int), Int] = {
case (a, b) if b != 0 => a / b
}
divide.isDefinedAt((10, 2)) // true
divide.isDefinedAt((10, 0)) // false
divide((10, 2)) // 5
```
## Exhaustive Patterns
Scala compiler warns about non-exhaustive matches with sealed types:
```scala
sealed trait Color
case object Red extends Color
case object Green extends Color
case object Blue extends Color
def describe(color: Color): String = color match {
case Red => "red"
case Green => "green"
// Compiler warning: missing Blue
}
```
## Summary
- `match` is Scala's powerful pattern matching expression
- Patterns include constants, variables, types, lists, tuples, and case classes
- Guards (`if`) add conditions to patterns
- Case objects represent singleton values in sealed hierarchies
- Extractors (`unapply`) enable custom pattern matching
- Sealed types enable exhaustive matching
- Partial functions handle subset-of-cases scenarios
- Pattern matching works in assignments and for comprehensions
Comments
Comments powered by Giscus
To enable comments, add your Giscus embed code here.
Learn more about Giscus →