26
votes

NOTE: I am asking this question out of inquisitiveness and not questioning the importance of a language feature.

Looks to be a great feature introduced to people from imperative world of programming. I am new to Scala and still trying to figure out where all, do its massive sets of constructs fit in and can be leveraged.

Pattern matching can definitely do stuff 100 x better than the switch case. but still, it is a case construct over which we use to prefer polymorphism since the time OOP came out.

So in short what I am finding difficult to understand is, If switch case encourages duplication and we better write case related code into respective classes then How does Scala's pattern matching overcome this ?

We can still have classes or generic classes for various cases and again leverage polymorphism to our need.

5
Short answer: algebraic data types. Pattern matching is a very simple and elegant way to work with ADTs. If your code is more OO-y, polymorphism is likely to be a better fit. - Travis Brown
"its massive sets of constructs" – don't let Martin Odersky read that. He's rather proud of the fact that Scala's set of constructs is much smaller than that of other languages. He prefers a language with a small number of powerful, composable, orthogonal constructs. By unifying e.g. functions and objects into a single construct or unifying modules, components and objects into a single construct. Or using inheritance to implement Algebraic Data Types. - Jörg W Mittag
@JörgWMittag: He has definitely achieved simplicity. but at the same time brought hell lot of new things for an average experienced Java developer. and all of them are really strong features but only if we take out time and understand them one by one. pure OOness blended with rich Functional features are really nice. but at the same time actors,pattern matching,FP itself, are so many new things that a Java developer can easily get confused. I get confused thinking whether to Do Scala the Java way Or jump into actors and pattern matching wherever and whenever I can. - Amogh Talpallikar
I don't get your question. Can you explain what you mean a) by "duplication", b) by "write type related code"? - 0__
@0__: I kind of got confused, when to go for pattern matching and when polymorphism, because I am not at all going to use it in place of switch because I dont use it at all. So I wanted to know what is the generic use case of pattern matching when writing OO code in Scala, other than for algebraic datatypes which is also their in most of the scala books. and agilesteel's answer clears that out for me. - Amogh Talpallikar

5 Answers

18
votes

It's the matter of the difference between objects and data structures.

If you are dealing with objects use the subtype polymorphism - adding new types doesn't require recompilation, retesting or redeployment of the existing ones, whereas adding a new algorithm (a method on the interface, which is at the top of the hierarchy) does.

If you are dealing with data structures use patter matching - adding new algorithms doesn't require recompilation, retesting or redeployment of the existing ones, whereas adding a new type does.

Read more about it here.

4
votes

Patter matching is a great feature because it is easy to use.

It solves the problem of "how to bring functionality to an object system" far better than most design patterns in widely used object-oriented languages. For example there is the Visitor pattern, which separates an algorithm from its object structure. The idea is great because it allows us to change behavior of our objects without touching their classes. But on the other side this pattern fails in overcomplexity and verbosity of notation. With pattern matching, this can be solved easily:

def calc(e: Expression): Double = e match {
  case Num(n) => n
  case Add(a, b) => calc(a)+calc(b)
  case Sub(a, b) => calc(a)-calc(b)
  ...
}

This separates the calculation of an AST from its definition and is much better to read than the polymorphic Visitor pattern.

Because patter matching is so easy, we can use it everywhere - you will find it on places which you have never thought of in most OO languages. A great example are Actors, which use algebraic data types (ADT) to communicate between each other:

sealed trait Message
case class Hello(name: String) extends Message
case class Bye(name: String) extends Message
case class Question(q: Symbol) extends Message

class MySelf extends Actor {
  def receive {
    case Hello(name) => println("Hello "+name)
    case Bye(name) => println("Buy "+name)
    case Question('AreYouOk) => sender answer "I'm ok"
    ...
  }
}

I wish you a lot of fun by implementing this with the Visitor pattern!

2
votes

I see few points where pattern matching completes OOP and allows for more modular programming.

  1. When you have a big project, you want to avoid putting "too much behaviour" inside your domain classes. You can move the behaviour outside, and typically have a method which receives a class at the top of a hierarchy and matches against the children classes.

  2. When you are using a specific libraries and you would like to add behaviour but you cannot modify the sources . You can also use implicit conversion for this, but in simple cases pattern matching is faster and easier.

To answer your question, I would probably say that you underestimate the code reusage pattern matching can bring: when you create a match block that creates a PartialFunction. If you need to reuse your pattern-matching blocks, you can use PartialFunction chaining, through the orElse method. This also brings benefits when designing a hierarchical set of handlers of a specific object, since the matches are executed in order.

1
votes

Inheritance and case constructs are both valid ways to achieve polymorphism. They are good in slightly different situations. Unlike inheritance based polymorphism, pattern match is not extensible, however often you don't need it to be. Many structures in functional programming, such as Option, Either or :: can be used more concisely with pattern matches that oop polymorphism and if statements. In general any problem could be solved with either type of polymorphism. It is only a matter of elegance.

1
votes

They don't they actually do have that exact problem if you abuse them.

Just as the polymorphy by inheritance gets into problems when it causes your classes to attract all kinds of methods that don't really belong into that class.

While Java has some reasonable strong support for inheritance the switch statement is just a joke.

In Scala you have even stronger support for inheritance and the amazing pattern matching.

It's your job to pick the right hammer for your nail.