Pattern matching allows you to check a value against a given pattern. It is similar to Java and C++ switch statement, but it’s more powerful.
The syntax for Scala pattern matching is given below:
val scores = Seq(30, 50, 60, 90) val score = scores(Random.nextInt(scores.length)) score match { case 30 => "F - Fail" case 50 => "C - Good" case 60 => "B - Very Good" case 90 => "A - Excellent" }
In the example, we create a value, score, that gets a random number taken from a sequence of four integers 30, 50, 60 and 90. score now becomes the left operand of the match operator and on the right we have an expression with four cases. Also note the the match expression is a value that can be assigned.
1. Matching Case Classes
Pattern matching is normally applied in case classes. Let’s assume we have this representation:
type Message =
Email String String String
| SMS String String
| AudioMemo String String
| Letter
This is and Elm syntax. You can learn Elm here.
The means that we have four classes: Email, SMS, AudioMemo and Letter. All of them are derived from Message. Also, the parameters for the classes are specified against each of them.
The Scala equivalent for this is given below:
sealed trait Message case class Email (sender:String, title: String, body: String) extends Message case class SMS (caller:String, message: String) extends Message case class AudioMemo (contact:String, link: String) extends Message case class Letter() extends Message
Now, we would like to display a custom message based on the particular class. Therefore we would do pattern matching on the base class, Message. We’ll write a function that would take an argument of type Message and display a particular output based on which implementation of message it is.
The function is given below:
def showMessage = (a: Message) => { a match { case Email(sender, title, _) => { println(s"You received a mail from $sender with $title") } case SMS(caller, message) => { println(s"An SMS arrived from $caller. Message: $message") } case AudioMemo(name, link) => { println(s"Audio message from $name available here: $link") } case Letter() => { println("Please check your postbox") } } }
Now we can call the function by and pass in a message type like so:
val myMail = new Email("Kindson", "Hello", "Love you" ) showMessage(myMail) val myLetter = new Letter() showMessage(myLetter)
I recommend you run this code yourself and see what the output looks like.
2. Matching on Only Types
In the example given in the preceding section, did pattern matching on the types as well as the arguments. But we can also perform pattern matching based on only the types. The code below illustrates that. Assuming we have two case classes Phone and Tablet that extends Device as shown below.
sealed trait Device case class Phone(make: String) extends Device case class Tablet(make: String) extends Device case class Laptop(make: String) extends Device
Now we write a function that returns a string based on the implementation of the device. Note the structure of the case statement that no argument was used
def tellMe(device: Device) = device match { case p:Phone => "This is a phone" case t:Tablet => "This is a tablet" case l:Laptop => "This is a laptop" }