Pattern matching is not new. It existed for decades as one of the most powerful functional programming technique. But it hasn't been seen before in mainstream languages. Scala takes the credit to bring pattern matching to the centre of the programming style. So, the next question is this.
What is Scala pattern matching?
When we talk about pattern matching, the first thing that comes to our mind is a string
matching or a regular expression.
But in the context of functional programming, this terminology takes a new meaning. Instead
of
regular
expression matching, the Functional Programming is going to look into matching objects
against
other
objects.
What does it mean?
The most fundamental example of object comparison is a type checking. Assume you got an
object,
and you want to test it for following possibilities.
- Does it match with a String Object?
- Does it match with an Integer Object?
- Does it match with a Double Object?
- Or Is it something else?
Here is the code for such a comparison.
This example is the most basic kind of pattern matching. We call it a typed pattern match.
We
use this kind of pattern matching
as a convenient replacement for type tests and type casts. If you want to do the same thing
in
Java,
you would be using a bunch of
instancOf tests. And when the test succeeds, you need to do a cast. But Scala
pattern
matching
gives you a convenient alternative which looks like Java's switch statement. But instead of
matching
numbers, that's what a switch does. You are matching object types. The Scala's pattern
matching
also
binds the value on a pattern match. In this example, when the object x matches with an
Integer
type,
Scala binds it to i. If it matches with a String type, Scala ties it to s. This automatic
binding
saves you from casting the x to a matching type.
The type test is a common problem. Let's say you are dealing with a JSON or an XML
object.
JSON
or XML is a tree of various data objects, and those individual objects may be a customer
name,
a
phone number or address. You don't know the type of each element, and hence you have a
problem.
The
only way to deal with them is to have a bunch of
InstancOf tests. In every case, you are asking a question. Is this a Phone number?
Is
this
a customer name? If the answer is yes, you need to cast it to a phone number object. This
approach
is rather ugly and clumsy. Pattern matching does the same thing in a much safer and more
natural
way.
The typed pattern match is just one. There are many other kinds of pattern matching. I
am
not
going to cover all of them here. But before we close this part, it is important to
understand
the
purpose of pattern matching.
Why do we need pattern matching?
If we take an object-oriented approach, we encapsulate everything into an object, the data
structure as well as the methods
to work on those data structures. Both are encapsulated to create an object. And then, we
don't
look
at the data structures within the object. We just use methods. Right?
When we need new functionality or a feature, we add new methods to the object
definition.
Correct?
This approach works perfectly fine if you have a fixed set of operations that you want to
perform
on the data structure. Even if you occasionally want to add new methods to the object, this
approach
is perfectly fine.
The problem comes when you want to perform new operations on those data structures all
the
time.
You might realize that you already have tens of methods defined, but now you need a new one.
You
keep discovering new requirements, and you may want few more methods and then some more a
few
days
later. Sometimes, you don't own the code, and it is not possible to add new members for some
reason.
What will you do in those situations?
The pattern matching gives you an alternative to handle this problem. The idea is simple
but
incredible. Deconstruct the object. I mean, when you create an object, you pass parameters
to
the
object and construct it. I am talking about reverse procedure. Take the data structures out
from
the object. If required, deconstruct those structures as well. Then, it becomes easy to
perform
the
necessary operations on those data structures. In a layman's language. If you can open a box
and
take individual items out, you can do a lot many things with those items. Right?
Scala pattern matching example
Let me give you an example. Assume you have a class to define a message.
The code for the class looks like this.
We have two data elements. The id of the person sending the message, and the message text
itself. Both items are string type.
Since id is a string, it can be an email address, a mobile number, a Skype id or may be
something
else. In fact, anything that we can represent as a valid String may be a sender's id.
Now, assume you have got a list of Messages. It may be something like this.
Some of them are email messages while others are SMS from a mobile phone. Now, we have a
question to answer.
Do we have two successive emails from the same person on this list?
When I say the same person, I mean lisa@yahoo.com is the same as lisa@gmail.com. Can you
create
a function to answer this question?
How will you do it? Think about it?Let me give you a step by step logic.
- Take two messages from the list. (Deconstruct)
- Extract the sender's id from both messages. (Deconstruct)
- Check if both are email addresses. (Type test)
- If yes, ignore the domain part and extract only the username. (Deconstruct)
- Check if both usernames are same. (if Condition)
- If yes, return a positive answer. (Action)
- If no, remove the head of the list and repeat with the tail of the list. (Deconstruct and repeat)
- If you reach the end of this list, return a negative answer. (Action)
Do you agree with the steps? They are straight forward. You can easily write a function
using
Java or any other language.
But that would be little ugly and clumsy. Pattern matching can give you a code that is
concise,
clean
and easy to follow.
If you carefully investigate the logic, there is nothing other than deconstruction, type
test,
and action.Let me explain this.
The first step is to take two messages from the list. In fact, it is deconstructing your
list
into three parts. The first message, the second message, and the remaining messages.
Step 2 is again a deconstruction of a message into id and the message text.
Step 3 is a Type test. If we have a class for the email address, then we want to check
if
the
given string is of an email address type.
Step 4 is once again deconstructing an email address to a username and domain name.
Step 5 is an if condition.
Step 6 is an action.
Step 7, this is again deconstructing your list into a head and the tail.
The point that I am trying to make is this.
You can sense a pattern matching solution when your logic is all about following things.
- Deconstructing objects
- Type checking
- Testing if conditions and taking Actions
That's what we have here in this requirement.
My function looks like this.
Doesn't it look simple, concise and straightforward? It is hard to explain the code without
going into lots of Scala language
details. We need few more lines of code to make this work. So, I am skipping the detailed
explanation
for now. We will revisit this example and pick up few more examples when we talk about
pattern
matching
and extractors in Scala language.
However, the complete code is listed below for your reference. The video tutorial
executes
the
code and shows the result.
Great. That's all about pattern matching for now.
Thank you for watching Learning Journal. Keep learning and Keep growing.