पाठ 10 / 25
Pattern Matching
Destructure data with match, guards, extractors and typed patterns.
match is a powerful expression
match compares a value against patterns and returns the result of the first matching case. Patterns include literals (case 0 =>), variables (case n =>), the wildcard _, typed patterns (case s: String =>), constructor patterns for case classes and enums (case Item(sku, qty) =>), tuples (case (a, b) =>), lists (case head :: tail =>, case Nil =>), alternatives (case "y" | "yes" =>), binders (case p @ Payment.Card(_) =>) and guards (case Item(_, q) if q > 10 =>). A match that finds nothing throws MatchError, and the compiler warns about non-exhaustive matches on sealed types. Extractor objects define unapply to make custom patterns, for example parsing a PIN code from a string. Pattern matching also appears in val (a, b) = pair, in for-comprehension generators and in partial functions ({ case ... } blocks) passed to collect. Prefer matching on ADTs over isInstanceOf checks and casts.
Pattern matching as a sorter
Each case checks shape and conditions, binding the parts it needs.
Patterns on lists, tuples, case classes and extractors
Guards, binders and a custom extractor.
case class Item(sku: String, qty: Int, pricePaise: Long)
def summarise(items: List[Item]): String = items match
case Nil => "empty cart"
case Item(sku, 1, _) :: Nil => s"just one $sku"
case first :: rest if rest.size > 5 => s"${first.sku} and ${rest.size} more (bulk order)"
case first :: rest => s"${first.sku} and ${rest.size} more"
object PinCode:
def unapply(s: String): Option[Int] =
if s.matches("""\d{6}""") then Some(s.toInt) else None
def classify(input: Any): String = input match
case 0 => "zero"
case n: Int if n < 0 => "negative number"
case PinCode(pin) => s"PIN code $pin"
case s: String if s.isBlank => "blank text"
case (city: String, count: Int) => s"$count orders in $city"
case item @ Item(_, qty, _) if qty > 100 => s"bulk item ${item.sku}"
case _ => "something else"
println(summarise(List(Item("pen", 1, 4950)))) // just one pen
println(classify("411001")) // PIN code 411001
println(classify(("Pune", 12))) // 12 orders in PuneOrder matters
Cases are tried top to bottom. Put specific cases (with guards) before general ones; a case _ or case first :: rest placed too early silently hides the cases after it, and the compiler warns that they are unreachable.
त्वरित जाँच: What happens when no case matches a value in a match expression?
- It returns null
- The first case is used
- A MatchError is thrown at run time
- The program fails to compile in every case
Answer
A MatchError is thrown at run time — Non-exhaustive matches can fail at run time with MatchError; sealed types let the compiler warn first.