What makes pattern matching and “implicits” in Scala so powerful?

Learn how to enable functional behavior in Scala code.

By Daniel Hinojosa
January 19, 2017
Stairs Stairs (source: Pexels vis Pixabay)

In my online class, Scala: Beyond the Basics, I discuss two very important aspects to functional programming in Scala. The first is pattern matching, which is the ability to take a pattern of how things are constructed and tear them apart and make decisions based on the parts that make up the item. For example, if I were to create a student registration system for a middle school, I may want to deconstruct the student (not literally) to find out the student’s last name and then put them into the corresponding line (i.e., A-F, G-N, O-Z).

Another aspect I talk about in the course are implicits. Implicits can be a powerful ally in functional programming, but, when used incorrectly, they can turn on you and make debugging your code difficult. With implicits, you can add methods to objects that already exist, establish conversion strategies, and even resurrect parameterized types that have been erased at runtime.  

Learn faster. Dig deeper. See farther.

Join the O'Reilly online learning platform. Get a free trial today and find answers on the fly, or master something new and useful.

Learn more

Let’s take a deeper look into what makes both of these features so powerful for Scala developers…

Pattern Matching

You can think of pattern matching as an if or a switch statement on functional steroids. With that said, pattern matching has always been something enjoyed by functional programmers.

Why pattern matching? For one reason, pattern matching is not only for primitive types or String like a switch statement in Java, it is for every conceivable object. If you come from a Java background, this is already a win. Pattern matching can be programmed to extract values given your own logic or use some of the predefined logic as already developed in the language.  

This goes further than a standard regular expression matching from a String. Whereas regular expressions match on a character stream, pattern matching matches on objects themselves. Pattern Matching tears apart the composable parts of an object so that you can analyze those components and make decisions based on those constituent parts. If those constituent parts have no relevance to your decision, you can just ignore them to get what you need.

Take a look at the following example in Scala:

def second[A](ls:List[A]):Option[A] = {
   ls match {
     case Nil => None
     case a :: Nil => None
     case a :: xs => Some(xs.head)
   }
}

Here we have a method labeled second that accepts a List and returns an Option of type A. One of the Option reasons for existence is to avoid null. An Option is a functional programming way to say, “Yes, I have an answer and here it is, or no, I don’t have an answer and you’ll get nothing further from me.”

The ls match is how we enter into pattern matching in Scala. From there we have subsequent case statements that in turn will determine if the given pattern will indeed match what we gave the match, and in this case it is ls.

The first case is Nil which means that if ls is an empty List, then we will return None which is our way of saying that no, there is no second in this List.

The second case is a {two-colons} Nil which by its pattern means that there is only one element since Nil means the end of the list. The element will be represented by a.

The third case is a {two-colons} xs which by this last pattern means that a will represent the first element and the xs will be the remainder of the list. If you are familiar with old style LISP this will be analogous to car and cdr.

Implicits

Another powerful feature in Scala is implicits.  However, implicits can be a double-edged sword.  Some aspects are pretty amazing, such as how they meld with type classes and add additional behaviors to preexisting classes, but other aspects can make Scala programs rather hard to read and debug. Regardless, implicits can be a tremendous tool if used in the right way.

implicits can be thought of as invisible hash maps that have a key as a type or class and a value of an object or a function.  These hash maps are bound to a scope, particularly where they are either created or imported.

Remember the operative word is “scope”. If you set an implicit at a particular scope it will be available at that scope.

This is how an implicit is established, in this case using an Int:

implicit val a = 100

Whatever scope this is in, we have an Int that is tied to 100. Whenever we need that Int, we can ask for it either in a method declaration, like in the following example:

def calculateWeeklyRate(hours:Int)(implicit rate:Int) = {
   hours * rate
}

Or the previous example can be called in the following manner:

calculateWeeklyRate(40) //4000

Notice that the rate is not set, that is what is meant by implicit it will obtain the rate from the environment where an Int type is bound to an object, in this case, that object is a 100.

We can also just ask for it using the method implicitly and get the object that corresponds to that type whenever we need it:

val result = 300 * implicitly[Int] //30000

Obviously, there is much more to both pattern matching and implicits, but this should be enough to whet your appetite. If you’d like to learn more, register for one of my online training courses on April 11-12 and May 16-17. You’ll receive some solid grounding to explore these two features in even greater depth.

Post topics: Software Engineering
Share: