Pattern Match Anything in Scala
For more concise and readable code, learn more about the power of the Scala’s pattern matching in different use cases in this article.
Join the DZone community and get the full member experience.
Join For FreeWhat Is Pattern Matching?
Pattern matching is a powerful feature of the Scala language. It is a mechanism for checking a value against a pattern. A successful match deconstructs the value into its constituent parts.
Pattern matching allows for more concise and readable code while at the same time provide the ability to match elements against complex patterns.
In this blog, we will see the power of the Scala’s pattern matching in different use cases.
Syntax:
The match expression consist of multiple parts:
- The value we’ll use to match the patterns is called a candidate
- The keyword match
- At least one case clause consisting of the case keyword, the pattern, an arrow symbol, and the code to be executed when the pattern matches
- A default clause when no other pattern has matched. The default clause is recognizable because it consists of the underscore character (_) and is the last of the case clauses
Example:
Here is a simple example to illustrate those parts:
Is match a Better Replacement for the Java switch Statements and if/else Statements?
match expressions can be seen as a generalization of switch statements and if/else statements. match expressions do everything that switch statements and if/else statements do, and much more.
However, there are a few major differences to keep in mind:
- First, match is an expression in Scala, it always results in a value.
- Second, match expressions can be used with any type. If you want to match on your types, go right ahead!
- Third, Scala’s alternative expressions never “fall through” into the next case. In Java, we must use explicit break statements to exit a switch at the end of each branch, or we will fall through to the next branch. This is annoying and error-prone.
- Fourth, if none of the patterns match, an exception named MatchError is thrown. This means we always have to make sure that all cases are covered, even if it means adding a default case where there’s nothing to do.
Patterns in match Expressions
Wildcard Patterns
The wildcard pattern ( _ ) matches any object whatsoever. It is used as a default case i.e., catch-all alternative. It can also be used to represent the element of an object whose value is not required.
Here is an example to illustrate wildcard patterns.
Constant Patterns
A constant pattern matches only itself. Any literal may be used as a constant.
Here is an example to illustrate constant patterns.
Variable Patterns
A variable pattern matches any object, just like a wildcard. But unlike a wildcard, Scala binds the variable to whatever the object is. So then, we can use this variable to act on the object further.
Also note that, in case of clauses, a term that begins with a lowercase letter is assumed to be the name of a new variable that will hold an extracted value. To refer to a previously defined variable, enclose it in back-ticks. Conversely, a term that begins with an uppercase letter is assumed to be a type name.
To avoid duplication, case clauses also support an “or” construct, using a | method.
Here is an example to illustrate variable patterns.
Constructor Patterns
Constructors are where pattern matching becomes powerful. Case classes are a special kind of classes that are optimized for use in pattern matching.
Pattern matching of case classes is also called deep matching, where we examine the contents of instances of case classes. Matching of case classes means to first check that the object is a member of the named case class, and then to check that the constructor parameters of the object match the extra patterns supplied.
Here is an example to illustrate constructor patterns.
Sequence Patterns
We can also match against sequences. Arrays, Lists, and Vectors consist of elements. These sequences and their elements are also used to form patterns.
We can use wildcards to specify any number of elements within the pattern. To match a single element, underscore wildcard ( _ ) is used. On the other hand, to match an unknown number of elements (zero, one, or more), star wildcard ( * ) is used.
Here is an example to illustrate sequence patterns.
List Patterns
List collection is a little different than other collections. It is built from “cons” cells and ends in a Nil element. We can match against Lists with List expressions, using :: operator. List patterns are used to deconstruct the List into its constituent parts.
Here is an example to illustrate list patterns.
Tuple Patterns
We can match against tuples too. Tuples are objects containing a limited number of sub-objects. We can imagine those as collections of mixed elements with a limited size. Tuple patterns are used to deconstruct the Tuple into its constituent parts.
Here is an example to illustrate tuple patterns.
Typed Patterns
Scala is a typed language, each object has a static type that cannot be changed. We can match on the type of an expression.
Here is an example to illustrate typed patterns.
The effect of match against a typed pattern is equivalent by using a type test followed by a type cast.
To test whether an expression has type String, we use: expr.isInstanceOf[String]
To cast the same expression to type String, we use:
expr.asInstanceOf[String]
Using a type test and type cast, we could rewrite the first case of the given match expression.
Option Type Patterns
Scala has a standard type named Option for optional values. Such a value can be of two forms: Some(x) object, where x is the actual value, or the None object, which represents a missing value.
Here is an example to illustrate option type patterns.
Conclusion
Pattern matching is a powerful “protocol” for extracting data inside data structures and objects. It makes idiomatic Scala code concise, yet powerful. Though, it’s not unusual for Scala programs to have 10 times fewer lines of code than comparable programs written in Java.
Opinions expressed by DZone contributors are their own.
Comments