8 Scala Pattern Matching Tricks
8 Scala Pattern Matching Tricks
In this article, we get you better acquainted to pattern matching in Scala, focusing on list extractors, Haskell-like prepending, type specifiers, etc.
Join the DZone community and get the full member experience.Join For Free
You can't be a Scala programmer and say you've never used pattern matching — it is one of the most powerful Scala features. It allows one to test lots of values and conditions without nested and chained if-else expressions.
This article is for the Scala programmer who is either, a) only getting started with Scala, or b) has only used pattern matching in its basic form — as a switch-on-steroids or to deconstruct case classes.
This article will give you some tools to use pattern matching to the max. You can also watch it in video form on YouTube or attached below:
1. List Extractors
Lists can be deconstructed with pattern matching in a number of powerful ways. Let me build a list:
You can extract any element out of this list with a pattern that looks like the case class constructor:
This pattern matches a list with exactly four elements, in which we don't care about the first two. The third one must be exactly 3, and the fourth can be anything, but we name it,
somethingElse, so we can reuse it in the s-interpolated string.
2. Haskell-Like Prepending
If I consider the same list as before, I can extract the head and tail of the list as follows:
Don't ask how this is possible yet. That will be the subject of an upcoming advanced article. The prepend pattern is often very useful in code that processes a list, but when you don't know in advance whether the list is empty or not, you write it like this:
This style of handling a list may be very familiar to those of you who know a bit of Haskell.
3. List Vararg Pattern
The first pattern we showed above can only constrain a list to a definitive number of elements. What if you don't know (or care about) the number of elements in advance?
_* is the important bit, which means "any number of additional arguments". This pattern is much more flexible because an (almost) infinite number of lists can match this pattern, instead of the 4-element list pattern we had before. The only catch with
_* is that it must be the last bit in the pattern. In other words, the case,
List(_, 2, _*, 55), will not compile, for example.
4. Other List Infix Patterns
It's very useful when we can test the head of the list, or even the elements inside the list. But, what if we want to test the last element of the list?
:+ is the append operator, which works much like
:: from the point of view of pattern matching. You can also use the
+: prepend operator (for symmetry), but we prefer
::. A nice benefit of the append operator is that we can combine it with the vararg pattern for a really powerful structure:
(Look for the
_*), which overcomes some of the limitations of the vararg pattern above.
5. Type Specifiers
Sometimes, we really don't care about the values being matched, but only their type.
:String bit is the important part. It allows the cases to match only those patterns that conform to that type. One very useful scenario where this is particularly useful is when we catch exceptions:
(Spoiler: catching exceptions is also based on pattern matching!)
The drawback with type guards is that they are based on reflection. Beware of performance hits!
6. Name Binding
I've seen the following pattern more times than I can count:
We deconstruct a case class only to re-instantiate it with the same data for later. If we didn't care about any field in the case class, that would be fine because we would use a type specifier (see above). Even that is not 100% fine because we rely on reflection. But, what if we care about some fields (not all) and the entire instance. Can we reuse those?
Answer: name the pattern you're matching (see the p @) so you can reuse it later. You can even name sub-patterns:
7. Conditional Guards
If you're like me, you probably tried at least once to pattern match something that satisfies a condition, and because you only knew the "anything" and "constant" patterns, you gave up pattern matching and used chained if-elses instead.
As you can see above, the if guards are there directly in the pattern. Also notice that the condition does not have parentheses.
8. Alternative Patterns
For situations where you return the same expression for multiple patterns, you don't need to copy and paste the same code.
You can combine the patterns where you return the same expression into a single pattern:
The only drawback of this pattern is that you can't bind any names because the compiler can't ensure those values are available on the right-hand side.
This pattern is useful in practice for a lot of scenarios, for example when you want to handle many kinds of exceptions:
Until Next Time
I hope it was useful, and you're better equipped to use pattern matching to the fullest! I've just started writing here and on the Rock the JVM blog, so leave your feedback in the comments, I read everything.
If you liked this, you can also read the 2-hour Scala at Light Speed comprehensive mini-series on Scala and functional programming.
Published at DZone with permission of Daniel Ciocirlan . See the original article here.
Opinions expressed by DZone contributors are their own.