4
votes

So, pattern matching in functional languages is pretty awesome. I wondering why most imperative languages haven't implemented this feature? To my understanding, Scala is the only "mainstream" imperative language that has pattern matching. The case/switch structure is just so much less powerful.

In particular, I am interested in whether the lack of pattern matching is because of technical reasons or historical reasons?

4
Why is Scala an Imperative[ish] Language? (Tongue in cheek, but the point is - someone design it that way.) - user2864740
All this is speculation, but I'd guess the respective type systems play a role. Many modern imperative languages have type systems that aren't as rigid as those of functional languages. Pattern matching quickly becomes confusing when the language has dynamic typing. - Wander Nauta
@WanderNauta I don't see how pattern matching would be affected more than other "just knowing what that variable/expression is" aspects of dynamic typing. As a counter-example: Clojure is dynamic, but supports pattern matching. - user2864740
@user2864740 why it's not? - om-nom-nom

4 Answers

5
votes

It's mostly historic. Pattern matching -- and more to the point, algebraic data types -- was invented around 1980 for the functional language Hope. From there it quickly made it into ML, and was later adopted in other functional languages like Miranda and Haskell. The mainstream imperative world usually takes a few decades longer to pick up new programming language ideas.

One reason that particularly hindered adoption is that the mainstream has long been dominated by object-oriented ideology. In that world, anything that isn't expressed by objects and subtyping is considered morally "wrong". One could argue that algebraic data types are kind of an antithesis to that.

Perhaps there are also some technical reasons that make it more natural in functional languages:

  • Regular scoping rules and fine-grained binding constructs for variables are the norm in functional languages, but less common in mainstream imperative languages.

  • Especially so since patterns bind immutable variables.

  • Type checking pattern matches relies on the more well-formed structure and rigidness of functional type systems, and their close ties to computational logic. Mainstream type systems are usually far away from that.

  • Algebraic data types require heap allocation (unless you want to waste a lot of space and prohibit recursion), and would be very inconvenient without garbage collection. However, GCs in mainstream languages, where they exist, are typically optimised for heavyweight objects, not lightweight functional data.

3
votes

Until recently (more precisely: until Scala), it was believed that pattern matching was incompatible with representation ignorance (i.e. the defining characteristic of OO). Since OO is a major paradigm in mainstream languages, having a seemingly irreconcilable feature in a mainstream language seemingly didn't make sense.

In Scala, pattern matching is reconciled with OO, simply by having the match operations be method calls on an object. (Rather simple in hindsight, no?) In particular, matches are performed by calling methods on extractor objects, which, just like any other object, only have access to the public API of the object being examined, thus not breaking encapsulation.

A pattern matching library inspired by Scala, in which patterns are first-class objects themselves (inspired by F#'s Active Patterns) was added to Newspeak, a very dynamic language that takes OO very seriously. (Newspeak doesn't even have variables, just methods.)

Note that regular expressions are an example of a limited form of pattern matching. Polymorphic method dispatch can also be seen as an example of a limited form of pattern matching (without the extraction features). In fact, method dispatch is powerful enough to implement full pattern matching as evidenced by Scala and especially Newspeak (in the latter, pattern matching is even implemented as a library, completely separate from the language).

0
votes

Here are my 2 cents. Take a simple Option pattern match:

val o = Some(1)

o match {
  case Some(i) => i + 1
  case None => 0
}

There are so many things going on here in Scala. Compiler checks that you have exhaustive match, creates a new variable i for the scope of the case statement and of course extracts the value from Option in a first place somehow.

Extracting a value would be doable in languages like Java. Implement unapply method(s) of some agreed upon interface and you are done. Now you can return values to a caller.

Providing this extracted value to the caller, which essentially requires a closure is not so convenient to do in regular OO languages without closure support. It can become quite ugly in Java7 where you would probably use Observer pattern.

If you add other pattern matching abilities of Scala in the mix like matching on specific types, i.e. case i: Int =>; using default clause _ when you want to (compiler has to check exhaustiveness somehow whether you use _ or not); additional checks like case i if i > 0 =>; and so on it quickly becomes very ugly from client side to use (think Java).

If you drop all those fancy pattern matching features your pattern match would be pretty much at the level of Java's switch statement.

It looks like it just would not be worth it, even if possible, to implement using anonymous classes without lambdas support and strong type system.

0
votes

I would say that it is more for historical than technical reasons. Pattern matching works well with algebraic data types which have also historically been associated with functional languages.

Scala is probably a bad example of an imperative language with pattern matching because it tends to favour the functional style, though it doesn't enforce it.

An example of a modern, mostly imperative language with pattern matching is Rust. Imperative and runs on the metal, but still has algebraic data types, pattern matching and other features that are more common to functional languages. But its' compiler implementation is a lot more complex than that of a C compiler