In this article, we will discuss match expressions and pattern matching in Scala. A match expression contains one or more case statements which are used to match against possible values. Match expressions in Scala are comparable to switch statements in other programming languages such as JavaScript or Java. They are an alternative to a series of if/else statements.
// switch statement in JavaScript
function dailyMessage(today) {
switch(today) {
case "Monday":
console.log("It's Monday! It's going to be a great week!");
break;
case "Tuesday":
console.log("Taco Tuesday!");
break;
case "Wednesday":
console.log("It's Wednesday, get over that hump!");
break;
case "Thursday":
console.log("It's Thursday! Friday's around the corner!");
break;
case "Friday":
console.log("TGIF!");
break;
case "Sunday":
case "Saturday":
console.log("It's the weekend!");
break;
default:
console.log("Not a valid day.");
}
}
dailyMessage("Thursday"); // prints "It's Thursday! Friday's around the corner!"
// match expression in Scala
val today: String = "Sunday"
val dailyMessage = today match {
case "Monday" => println("It's Monday! It's going to be a great week!")
case "Tuesday" => println("Taco Tuesday!")
case "Wednesday" => println("It's Wednesday, get over that hump!")
case "Thursday" => println("It's Thursday! Friday's around the corner!”)
case "Friday" => println("TGIF!")
case "Saturday" => println("It's the weekend!")
case "Sunday" => println("It's the weekend!")
case _ => println("Not a valid day.")
}
dailyMessage // prints "It's the weekend!"
Match expressions have some differences from switch statements, as follows:
- Match expressions always return a value
- There is no automatic fall through to the next statement
- No default case is required
If no cases match, a MatchError
is thrown.
Match expressions become most powerful with the use of pattern matching. Scala offers a rich set of patterns to match with inputs. Some common patterns are listed below.
Wildcard Pattern
The underscore _
is known as the wildcard pattern. The wildcard pattern matches any input. Recall that if no match is found in a match expression, a MatchError
is thrown. Thus, the wildcard pattern is often used as the final case to act as a “catch-all”.
val today: String = "Monday"
val dailyMessage = today match {
case _ => println("Today is the day!")
}
dailyMessage // will always print "Today is the day!"
Note that the wildcard pattern cannot be referenced within the return statement, so the following is invalid:
val today: String = "Monday"
val dailyMessage = today match {
case _ => println(s"Today is $_")
}
dailyMessage // will not compile
To capture and reference the input, use a variable pattern.
Variable Pattern
The variable pattern is denoted by any variable starting with a lowercase letter (e.g. input
). The variable pattern will match any input, and allow you to reference the captured input in the return statement.
val today: String = "Monday"
val dailyMessage = today match {
case dayInput => println(s"Today is $dayInput!")
}
dailyMessage // prints "Today is Monday!"
Stable Identifiers
Stable identifiers are used to match any constant such as a string or integer value. To match with a val, you must enclose the name of the val with backticks (e.g. `day`
), otherwise it is interpreted as a variable pattern (see above) which matches any input.
val today: Int = 2
val dailyMessage = today match {
case "Monday" => println("Today is Monday!")
case 2 => println("Today is Tuesday!")
}
dailyMessage // prints "Today is Tuesday!"
val monday: String = "Monday"
val today = "Tuesday"
val dailyMessage = today match {
case monday => println("Today is Monday!") // will not match the val above!
case "Tuesday" => println("Today is Tuesday!")
}
dailyMessage // will always print "Today is Monday!"
val monday: String = "Monday"
val tuesday: String = "Tuesday"
val today: String = "Tuesday"
val dailyMessage = today match {
case `monday` => println("Today is Monday!") // will match val monday
case `tuesday` => println("Today is Tuesday!") // will match val tuesday
}
dailyMessage // prints "Today is Tuesday!"
Type Matching
You can use pattern matching to match only the type of the input, as seen below:
case class Day(today: String)
val today = Day("Monday")
val num = 56
def dailyMessage(dayInput: Any): Unit = dayInput match {
case day: Day => println("Today is the day!")
case _ => println(s"$dayInput is not a valid day.")
}
dailyMessage(today) // prints "Today is the day!"
dailyMessage(num) // prints "56 is not a valid day."
Deconstructing Tuples
Tuples can be deconstructed to match one or more elements in the Tuple. Tuple patterns can be combined with other match patterns (e.g. wildcard, variable, stable identifiers, etc.).
val day1 = ("Monday", 1)
val day2 = ("Tuesday", 2)
val day3 = ("Tuesday", 16)
val day4 = ("Wednesday", 17)
def dailyMessage(dayInput: Any): Unit = dayInput match {
case (day, 1) => println(s"The first day of the month is a $day")
case ("Tuesday", num) => println(s"It's Tuesday, day $num.")
case (_, num) => println(s"Day $num.")
case _ => println("Not a valid day.")
}
dailyMessage(day1) // prints "The first day of the month is a Monday"
dailyMessage(day2) // prints "It's Tuesday, day 2"
dailyMessage(day3) // prints "It's Tuesday, day 16"
dailyMessage(day4) // prints "Day 17"
Pattern Alternative (OR)
val today: String = "Sunday"
val dailyMessage = today match {
case "Monday" | "Tuesday" | "Wednesday" | "Thursday" | "Friday" => println("It's a weekday!")
case "Saturday" | "Sunday" => println("It's the weekend!")
case _ => println("Not a valid day.")
}
dailyMessage // prints "It's the weekend!"
Pattern Guards
A case can be followed by if (some condition)
to match input only if a certain condition is met.
val monday: String = "Monday"
val wednesday: String = "Wednesday"
def dailyMessage(dayInput: String) = dayInput match {
case day if day.length > 6 => println("It'll be a long day today.")
case day if day.length <= 6 => println("Today won’t be so long!")
}
dailyMessage(monday) // prints "Today won't be so long!"
dailyMessage(wednesday) // prints "It'll be a long day today."
Conclusion
In conclusion, Scala offers a robust set of patterns for powerful pattern matching in your code. There are more patterns not discussed here which I encourage you to research on your own such as constructor patterns, sequence patterns, pattern bindings, and so on.