Pattern Matching Versus Polymorphism


#1

In Scala books, pattern matching is compared with Polymorphism from OO paradigm.
How are these two concepts comparable?


#2

Can you give a more concrete example of what you’ve seen? You say “in Scala books”, but I’ve never seen this said. I suspect they’re trying to make some more-specific point…


#3

Following is from ‘Programming Scala’ by Dean Wampler:

Pattern Matching Versus Polymorphism
Pattern matching plays a central role in functional programming just as polymorphism
plays a central role in object-oriented programming. Functional pattern matching is
much more important and sophisticated than the corresponding switch/case statements
found in most imperative languages, like Java. We will examine Scala’s support
for pattern matching in more detail in Chapter 8. In our example here, we can begin to
see that joining functional-style pattern matching with object-oriented polymorphic
dispatching is a powerful combination that is a benefit of mixed paradigm languages
like Scala.


#4

Your quoted text says, as far as I understand it, that the two concepts are similarily important in the respective paradigm and goes on to say that combining both is powerful. So it’s not an “Either pattern matching or polymorphism” choice, which the title could be interpreted to mean.

That said, one similar usage of subtyping polymorphism and pattern matching is having behaviour depend on the actual type. With polymorphism, different subclasses can have different behaviour on method calls, with pattern matching, a method can behave differently depending on which type is passed in. So one could argue they both serve that purpose, with different scope (in type vs. at use site of type).

For example, look at Option, a type that can be either a Some (containing a value) or a None (containing no value). Simplified implementation:

sealed trait Option[+A] {
  def map[B](f: A => B): Option[B]  //transform a value, if present
}
case class Some[+A](a: A) extends Option[A] { ... }
case object None extends Option[Nothing] { ... }

In an implementation using subtyping polymorphism, one would implement map for Some and None on these subtypes:

case class Some[+A](a: A) extends Option[A] {
  override def map[B](f: A => B): Option[B] = Some(f(a))
}
case object None extends Option[Nothing] {
  override def map[B](f: A => B): Option[B] = None
}

But you could as well implement them in the Option trait, using a pattern match to determine, if the method is called on a Some or a None:

sealed trait Option[+A] {
  def map[B](f: A => B): Option[B] = this match {
    case Some(a) => Some(f(a))
    case None => None
  }
}

(note that this example demonstrates subtyping polymorphism, but both variants use parametric polymorphism, i.e. methods with type parameters)