Let's play with pattern matching in Scala

Posted by Nicolas Kosinski on 2016-09-14 Translations: fr

Pattern matching is a powerful technique for filtering and testing variables. This article aims at illustrating pattern matching in Scala using a simple example. Let's say we want to return the color (red or black) of a playing card.

Notes: The code below "shows" the console output using comments (ex: "foo" // foo). I have used a IntelliJ IDEA worksheet but the standard Scala console can also be used (REPL FTW!)


Step #1: let's create a simple class

Let's create a immutable class for the "club" suite:

class Club {
  val symbol = "♣"
  val label = "club"
}

val club: Club = new Club()
club.symbol // ♣

So far, so good. Instances can be compared by identity (reference) but not by value because we did not override the equals method:

club.equals(club) // true
club == new Club // false
club.eq(new Club) // false
club.equals(new Club) // false

hashCode and toString are also default ones:

club // Club@1ac88f64
club.hashCode() // 1976061787
new Club().hashCode() // 1751431390


Step #2: let's create a case class

A case class has two benefits:

  1. auto-implement equals, hashCode and toString methods.
  2. enhance pattern matching capability via two methods: a "constructor" method, apply, and a "de-constructor" method, unapply.
case class CardSuite(symbol: String, label: String)

apply method is a kind of free constructor. By the way, we don't need to use the new keyword:

CardSuite("♣", "club") // CardSuite(♣,club)

equals, hashCode and toString methods are also implemented for free:

CardSuite("♣", "club").symbol
CardSuite("♣", "club") == CardSuite("♣", "club") // true
CardSuite("♣", "club").equals(CardSuite("♣", "club")) // true
CardSuite("♣", "club").eq(CardSuite("♣", "club")) // false
CardSuite("♣", "club").hashCode() // 1302714609
CardSuite("♣", "club").hashCode() // 1302714609


"Bonus" step: use an enumeration

Since there are four suites in French playing cards, we can create an enumeration. This is not directly related to our pattern matching example, but let's do it, for fun and profit. ;-)

object CardSuites {
  val CLUB = CardSuite("♣", "club")
  val DIAMOND = CardSuite("♦", "diamond")
  val HEART = CardSuite("♥", "heart")
  val SPADE = CardSuite("♠", "spade")
  def values() = List(DIAMOND, HEART, SPADE, CLUB)
}
CardSuites.CLUB != CardSuites.DIAMOND // true
CardSuites.values // List(CardSuite(♦,diamond), CardSuite(♥,heart), CardSuite(♠,spade), CardSuite(♣,club))


Last step: let's use pattern matching!

First example

Here is a first pattern matching example, used in a function that returns the color of a suite card:

def justColor(cardSuite: CardSuite): String = cardSuite match {
  case CardSuites.CLUB | CardSuites.SPADE => "black"
  case CardSuites.DIAMOND | CardSuites.HEART => "red"
  case _ => "none"
}

justColor(CardSuites.SPADE) // black

This example demonstrates:

  • the | notation (disjunction) that can be used to group several cases;
  • the _ notation (wildcard) for "other cases".

Second example

Here is a second example to demonstrate field filtering, also known as "de-structuring":

def describeColor(cardSuite: CardSuite): String = cardSuite match {
  case CardSuite(_, label) => s"$label is ${justColor(cardSuite)}"
}

describeColor(CardSuites.SPADE) // spade is black

We only keep the suite label using the unapply method of our case class.

That's all folks! 🤓

PS: Thanks to Jérôme Prudent for the Scala tips and for the review. Jérôme contributes to the Arolla blog (direct link to his posts).