Java developers often rely on switch/case statements when evaluating one of a set of possible values. Such evaluation is done, for instance, when parsing a tree structure. Switch and case have many limitations in Java—case can only take an int, for example—and many developers have come to believe that frequent uses of switch/case makes large code bases fragile.
Scala provides pattern matching as a more elegant and extensible way to accomplish what switch/case can do in Java. Case classes are used in pattern matching, and allow the compiler to help implement proper pattern matching. In chapter 15 of Programming in Scala, Martin Odersky, Lex Spoon, and Bill Venners describe case classes as:
Case classes are Scala’s way to allow pattern matching on objects
without requiring a large amount of boilerplate. In the common case, all you
need to do is add a single case keyword to each class that you want to be
pattern matchable...:
case class Var(name: String) extends Expr
Classes with such a modifier are called case classes. Using the modifier makes the Scala compiler add some syntactic conveniences to your class.
For instance, the compiler adds a factory method with the name of the class, makes all parameter values to the class vals (they can't be re-assigned), and adds sensible toString(), hashCode(), and equals() method implementations to the class. These changes together facilitate case classes to be used in pattern matching.
In a recent blog post, Are Scala's Case Classes a Failed Experiment?, Cedric Beust wonders if case classes, as implemented in Scala, actually achieve their goal of simplifying pattern matching:
Ever since I first read about case classes, I have been confused about their utility, and my puzzlement has not abated. Either I am missing something big or this feature is something that has been vastly overhyped and that should be avoided as much as possible...
When I exposed the crux of this argument to a few Scala experts, they overall agreed about the point and responded by pointing out that case classes were more useful as an alternative to the Visitor pattern.
Before turning our attention to this specific case, it's important to note that at this point, we are now looking at solving a niche problem. And quite a rare one, in my experience. If this is really the reason why case classes were invented, I am really left scratching my head about the decision to include such a big feature inside a language to solve such a small class of problems.
Visitors are used to emulate multiple dispatch in languages that don't support it natively. In some way, you are extending virtual invocation to apply to the runtime type of parameters passed to your functions...
Beust's argument centers around examples in the online Scala documentation that can actually make an object model more interdependent and, hence, harder to maintain.
As the volume of comments on that post indicated, 'no'. Yet there doesn't seem to be anything other than verbatim quoting of Cedric's original post here.
Here's the comment I just sent to the original blog.
Cedric, I think you are misrepresenting the various comments, interpreting them in a more negative light than they were intended.
Let me relate my experience to you: As you know I wrote the javac compiler with visitors and various ad-hoc solutions and I wrote most parts of the scalac compiler with case classes everywhere. The difference needs to be experienced to be believed. I would never, ever go back to a language that did not have case classes (or some equivalent) and pattern matching. Not if I wanted to write a compiler, or any other software that analyzes and transforms symbolic information.
Your argument concerning evolution is valid, but it has already been addressed in Scala with extractors. Read the ECOOP 2007 paper by Emir, Odersky and Williams.
The subject indicated an interesting read; something I'd been wondering myself, though mostly out of ignorance of the subject. Then I see, "...Cedric Beust...". Ah, well that explains it.
Thanks for the response, Martin. It might surprise you, but I have actually read most of the articles that I found in the documentation, but as you point out, it looks like case classes is something that needs to be experienced, and I have obviously not done that to the same extent that you have.
Hmmm. I'm just "barely" able to follow along and sort of understand where one might use these case classes, but do have to wonder how common it would be for people to use them appropriately in every day software development. (unless your days are spent solving a somewhat complicated set of problems such as when writing parsers / "compilers" and such)
They probably are very useful, but I wonder if it's possible that people are spending so much time in the theoretical/low level language workings that they lose sight of how useful features are to everyone else.
Only time will tell, but it doesn't sound very convincing to say this is a very obviously useful ~language level~ feature that everyone will benefit from. Not that I know very much about the subject or thought processes people go through when evaluating inclusion/exclusion of language features from a language creator POV.
please don't take my comments too seriously as I don't know very much about the subject, just observing.
I think that pattern matching, like object-orientation or functional programming is one of those programming paradigms which don't present obvious benefits until you've mastered at least the basics. I doubt many people who know how to use regex effectively would argue that it's of little value but ask someone who doesn't know how to use it and they might ask what it's good for.
I don't understand the full scope of case classes in Scala but the nice thing is that they still provide value even if you just use them as a replacement for the visitor pattern.
From a usability perspective, pattern matching in general tends to have a (roughly) parabolic readbility/power curve (and not in a good way). From some things I've seen on the email list, I'd have to say Scala's pattern matching is not an exception.
I'd say that this might create problems for novice users but I'm resigned to the belief that Scala will never be used widely by non-expert developers.
> Thanks for the response, Martin. It might surprise you, > but I have actually read most of the articles that I found > in the documentation, but as you point out, it looks like > case classes is something that needs to be experienced, > and I have obviously not done that to the same extent that > you have. > > -- > Cedric
Hi Cedric,
I find I use pattern matching quite a lot when programming in Scala. I occasionally define my own case classes, but more often use case classes already defined by others, mostly from the Scala API. One way I would describe the use case is by an analogy with exception classes in Java. Occasionally you write your own exception classes in Java, but more often you use exception classes written by others, especially from the Java API. When you write catch clauses for them, you're doing a kind of pattern match. Although it's done rarely in practice, because usually the class of the exception is sufficient information to enable you to handle it, inside a catch clause you *can* extract extra information out of an exception to help you decide how to handle it. That's similar to the way you can extract information out of a pattern in a Scala pattern match, which you can then use in the "handler" expression to the right of the => symbol in a Scala case clause.
So the analogous question becomes, why do we do exceptions that way in Java? Why don't we do it the more object-oriented way of defining the handler method on the exception class itself? Since the exception class has the data, the code that uses that data should be in that class, right? That's the OO way. The reason is that the author of the exception class doesn't know how people want to handle it. So the exception class allows people to extract its data instead of making it private. Code to handle the exception is outside the exception class, in catch clauses all over the place.
The Some/None case classes, which are the only two subclasses of Scala's Option type, are similar. When Martin Odersky wrote those classes, he didn't know the many ways you or anyone else would want to handle your optional data throughout your Scala programming career, so he couldn't put the millions of handler methods inside those classes. So instead in Scala you can extract the payload out of a Some, and handle that case and the None case very succinctly with a pattern match.
On the other hand, I think that sometimes even when you do know how you want to handle things, it can be more understandable or convenient to implement the functionality with case classes that you pattern match on. The two places I've done my own case classes (intended for pattern matching) were internal data structures, private to a package. So the impact of change is minimized, and the code is very simple and clear. It isn't as OO as it could be, but I have found it to be very practical.
Lastly, like others have mentioned, sometimes I make a case class just to get at the stuff you get for free, even though I never intended to pattern match on it. You get a factory method, for example, that allows you to construct a new instance without saying "new". It's handy.
I don't remember all the details, but for one project I wrote some sort of "DataEntryException" that know about the Swing Component that triggered it and whose default handle method was to flash that Swing Component and then put up a dialog box with the getMessage(). What I remember:
1) Even to me, who tends not to worry much about mixing layers, it felt "fishy" to be mixing error handling and Swing code. Purists would be truly outraged. 2) It worked incredibly well. The code became way shorter and simpler, the UI much more uniform.
I think it is difficult (for me anyway) to see the utility of a new and very different feature until I've experienced it. The new feature demands I change my perspective toward problem solving. Until I can do that, it will seem alien to me.
I don't think Scala has had enough exposure yet to determine if Case Classes are broadly useful.
> I don't remember all the details, but for one project I > wrote some sort of "DataEntryException" that know about > the Swing Component that triggered it and whose default > handle method was to flash that Swing Component and then > put up a dialog box with the getMessage(). What I > remember: > > 1) Even to me, who tends not to worry much about mixing > layers, it felt "fishy" to be mixing error handling and > Swing code. Purists would be truly outraged. > 2) It worked incredibly well. The code became way shorter > and simpler, the UI much more uniform.
Morgan, is the point here that Java's exception handling mechanism uses a basic form of pattern matching? If so, goo point. I think it's good to keep that in mind when talking about the usefulness of this feature.
Cedric's blog has a bunch of good replies on this too.
The point that case classes provide a nice way to implement the Visitor pattern resonates most with me.
On the other hand, statements like "case patterns implement Abstract Algrebraic data Types" make me go what??! Show how it's useful in your typical programming to convince folks.
I found the use of case classes/pattern matching made elegant code when using lists and recursive functions.
There are examples of this in chapter 16 of Programming in Scala. I would not want to copy the exact functions here, but here is the general usage:
def recursiveFunction(aList: List[T]): List[T] = aList match {
case List() => ...
case head :: tail => head doSomethingWith recursiveFunction(tail)
caseelse => ...
}
The elegance of it makes it a worthwhile feature for me 8)
> Morgan, is the point here that Java's exception handling > mechanism uses a basic form of pattern matching? If so, > goo point. I think it's good to keep that in mind when > talking about the usefulness of this feature.
I was mainly responding to Bill's comment about logic being split up, partly in the exception, and partly in the catch clauses. But I think the answer to your question is yes. The "patterns" of simple bad data entry (like "please order -4 of these things") were easy to catch, while other errors (in my case, none were expected, but one could imagine network crashes, invalid passwords, inventory being out of stock) get passed along for others to handle.
> > Morgan, is the point here that Java's exception > handling > > mechanism uses a basic form of pattern matching? If > so, > > goo point. I think it's good to keep that in mind when > > talking about the usefulness of this feature. > > I was mainly responding to Bill's comment about logic > being split up, partly in the exception, and partly in the > catch clauses...
When you mentioned catch clauses, it occurred to me (again) that Java already has pattern matching in a limited form.