There are a lot of cases when you need to make a decision in a program based on some condition or value. The first thought which comes to mind is an IF ELSE operator or something like SWITCH in Java. But Scala offers a more powerful and elegant approach for the handling of such situations. Let’s consider how pattern matching can help you.

Here is a simple demonstration of pattern matching:

1 + 2 - 3 + 4 - 5 match {
  case -1 => "minus one"
  case 0  => "zero"
  case 1  => "one"
  case _  => "unknown" 
} 
//minus one

In the first line we define an algebraic expression, then we use a special keyword match. After that follows curly bracket. In lines 2, 3, 4 and 5 we put possible values. They represent cases which we expect to get as a result of expression evaluation. Finally, the curly bracket is closed.

Summed up here is the general syntax for pattern matching:

pattern-matching-syntax

We can match any expression, then in case sections we describe what we expect to get. If the expression matches some particular case, pattern matching stops and returns an expression which corresponds to the matched case. If there is no match, then a MatchError exception is thrown. That’s why, as a rule, in the last case line, developers put an underscore; it serves as a default expression.

Pattern Matching Examples

Let’s examine several samples where pattern matching is used. The first example is the most simple. We are going to match expressions to constants.

val boo: Boolean = 5 < 10

boo match {
  case true => 1
  case false => 0
}
//1

In the next example we place pattern matching in a function and use an underscore in case all previous cases are not matched.

def matchFunction(v: Int) = v match {
  case 1 => "one"
  case 2 => "two"
  case _ => "unknown number"
}

matchFunction(2) //two
matchFunction(5) //unknown number

Now we can continue with more complex samples. Let’s apply pattern matching to custom classes:

trait Payment {
  def pay(amount: Double): Unit
}

class Cash extends Payment {
  def pay(amount: Double): Unit = println(s"Pay with cash $amount")
}
class CreditCard extends Payment {
  def pay(amount: Double): Unit = println(s"Pay with credit card $amount")
  def verify(): Unit = println("Verification...")
}

def processPayment(amount: Double, method: Payment) = method match {
  case c: Cash => c.pay(amount)
  case cc: CreditCard => cc.verify(); cc.pay(amount)
  case _ => println("Unknown payment method")
}

val paymentA = new Cash
val paymentB = new CreditCard

processPayment(10, paymentA) 
//Pay with cash 10.0

processPayment(50, paymentB)
//Verification...
//Pay with credit card 50.0

Based on these three examples we can cover most parts of real life scenarios. Furthermore, pattern matching can be nested. But, as is the case with IF-ELSE when level of nesting exceeds 2, the code can become hard to read.

Collection Pattern Matching

You can apply pattern matching to collections. It’s very convenient to deal with collection elements using pattern matching. Here is basic example:

val commonList = List(1, 2, 3, 4, 5)
val emptyList = Nil
val oneElement = 'a' :: Nil

def checkList[T](list: List[T]): String = list match {
  case Nil => "Empty list"
  case list :: Nil => "One element"
  case _ => "More than one element"
}

checkList(emptyList) //Empty list
checkList(oneElement) //One element
checkList(commonList) //More than one element

Here is how you can use pattern matching in a recursive sum of integers.

val commonList = List(1, 2, 3, 4, 5)
def sum(list: List[Int]): Int = {
  def recursion(sum: Int, list: List[Int]): Int = list match {
    case Nil => sum
    case el :: tail => (recursion(sum + el, tail))
  }
  recursion(0, list)
}

sum(commonList) //15

That’s why pattern matching so useful in Scala. It’s very handy to operate with values without casting them using instanceOf. Also it’s much more readable than IF-ELSE chains.

Write in the comments about personal cases where you find pattern matching helpful!