Scala Wonderland: Case classes and pattern matching

Pattern matching is usually related to text search. In Scala it has much more sophisticated usage. You can write exciting decision logic when used together with case classes. Even after understanding what the two things mean I wasn’t able to use them as they deserve. It takes a while to really grasp them. Long and winding road.

Case classes allow easy pattern matching when otherwise complicated code would had to be written. See official documentation for introduction. Let’s look at some more interesting examples.

Exceptions are case classes

case class MyException(msg: String) extends Exception(msg)

The reason is that exception catching is in fact pattern matching. Catch block contains patterns and if there is a match then related piece of code is executed. Following code demonstrates this. The second case matches when the exception is either RuntimeException or IOException.

try { ...
} catch {
  case ex: MyException => Logger.error(ex.toString)
  case ex @ (_ : SQLException | _ : IOException) => println(ex)
}

Plain old data holders
If a class is designed to be just a data holder without any methods, it’s recommended to be a case class. It is syntactically easier to construct new instance and constructor parameters of a case class are by definition accessible from outside. Also they can be structurally decomposed using pattern matching. This is very handy.

Structural decomposition
Pattern is used not only to specify the conditions, but also to decompose the object being matched. Following example tries to find a tuple in a map based on provided key. If it finds one, it returns the second item of the tuple, which is a string. That’s the decomposition. In case it doesn’t find anything, it returns N/A. If you are curious why there are double brackets ((…)) then the reason is that the outer brackets are to denote function call and the inner brackets represent a tuple of two items.

def getValue(key: Int, data: Map[Int, (Int, String)]): String = {
  data.get(key) match {
    case Some((num, text)) => text
    case _ => "N/A"
  }
}

These two creatures occur more and more in my code. Yes, and not to forget, if you check the previous code you can see that the function should return a string, but there is no return statement. In Scala resulting value is the value of the last statement. Here we have more last statements depending on the pattern matching, but we cover all possible execution paths and we always return a string. Compiler is happy and we are too.