Writing some code at home, in Java, I came across the problem of trying to compute a value based on the data of another value. This isn’t nothing exciting in on itself, but this whole project I was writing was about trying to write in functional style, as much as possible, in Java 8.
Also, being a fan of fluent interfaces, or readable code in general, this was one of the cases I really missed Scala’s pattern matching capabilities.
So I set to the task of writing a small utility that will allow me to emulate Scala’s pattern matching syntax and behavior, but in Java, at least to some extent.
The end goal I wanted to achieve was to write something similar to:
(you can probably guess the project was something about chess)
In this case
p was the parameter passed to a function, which consisted of this call alone. An equivalent piece of code would be to test for the class of
p – a series of if-elses, or store this mapping in a map. These are all valid solutions, but I wanted something that would be both concise, and didn’t require extra data preparation. Also something that would be more generally usable than in this case. Besides, I wanted to invent some new way to do this stuff, have some fun doing it.
So I ended up with writing this small utility, which allows for this kind of code:
This code implements a “micro-dsl” of sorts for pattern matching on the data.
The usage of the builder pattern, with 3 internal classes, is to allow for the fluent interface, which was ultimately my goal: enable easy to follow syntax for this kind of operation.
Note that the code makes use of Java 8’s functional interfaces of Predicate and Function, and of course the lambda expression syntax. This allows both for flexibility (you can write any expression you want) as well as concise code.
You can even use it recursively, as the following simple implementation of factorial shows:
It’s not perfect, as it does still tend to be verbose, and not nearly as neat as Scala’s pattern matching, but I believe it does allow for cleaner code, and improves the situation quite a bit for this type of tasks. Note also that this code is pretty simple and self contained – it doesn’t rely on any external library, besides the standard JDK (I did use some trivial utility functions there, e.g.
requireNotNull but that’s really beside the point).
Some points where I see this can improve are:
- The ability to provide a default match (an “otherwise” case) that will handle all unmatched cases
- The ability to provide the data to match as a lazily evaluated function, to allow for optimization (parallel evaluation of the cases?)
- A “vectorized” version of this, allowing to specify a pattern match over a complete stream of values, resulting in another stream of values
I will probably do these when I have some more time and the need arises. In the meantime, keeping it simple.
Full source, with updates, is available here.