Why don't imperative languages have pattern matching?
Asked Answered
E

4

6

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?

Earldom answered 31/7, 2014 at 19:20 Comment(4)
Why is Scala an Imperative[ish] Language? (Tongue in cheek, but the point is - someone design it that way.)Lear
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.Catricecatrina
@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.Lear
@Lear why it's not?Aronow
V
5

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.

Virology answered 1/8, 2014 at 7:55 Comment(2)
From the wikipedia article on 'Pattern Matching': "The first computer programs to use pattern matching were text editors.[citation needed] At Bell Labs, Ken Thompson extended the seeking and replacing features of the QED editor to accept regular expressions. Early programming languages with pattern matching constructs include SNOBOL from 1962, SASL from 1976, NPL from 1977, and KRC from 1981." Also Prolog has pattern matching via unification since 1972.Casas
@ShonFeder, fair enough, but the term "pattern matching" is rather overloaded and fuzzy. In the context of functional languages, it's usually referring to the construct that discriminates cases of (algebraic) data types and generalises constructs like if, case, or switch. AFAII that was novel in Hope.Virology
M
3

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).

Montagnard answered 1/8, 2014 at 10:17 Comment(3)
You make it sound like Scala first invented user-defined matching, a.k.a. views, to reconcile pattern matching with data abstraction. That's definitely not the case, see e.g. Wadler's 1987 paper on views (homepages.inf.ed.ac.uk/wadler/topics/language-design.html#views), which was only the first in a long list.Virology
@AndreasRossberg: is this about abstract data types or objects? I.e. type-name based abstraction or procedural abstraction?Bookmobile
Does it matter? The problem is the same whether you have intensional or extensional data abstraction. The solution will naturally be using the predominant mechanism, i.e., an OO language would use methods rather than functions for view transformations. No surprise there.Virology
X
0

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.

Xerophilous answered 1/8, 2014 at 3:5 Comment(2)
Your answer is specific to how Scala does pattern matching. The pattern matching in typical functional languages is significantly simpler, and doesn't require closures (but is still far better than a switch).Virology
@AndreasRossberg the question specifically mentioned Scala as a mainstream OO language which supports pattern matching, so it's relevant to talk about Scala's implementation of pattern matching here.Defiance
S
0

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

Strontium answered 1/8, 2014 at 7:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.