How Interfaces May Eliminate Need For Pattern Matching (sometimes)
For about a year, all Java code I’m writing heavily uses functional programming approaches. This provides a lot of benefits, but description of this new style is a topic for a separate long article (or even series of articles).
Now I’d like to focus on one curious observation: sometimes OO provides a more convenient way to use even purely FP concepts like monads.
Monads is a very convenient design pattern, often used to represent special states of values — potentially missing values (
Option) or results of computations which may fail (
Usually such a monad can be implemented using Algebraic Data Types, in particular, Sum Types.
To illustrate the concept, let’s imagine that we are implementing Java 8
Optional from scratch:
Now we need two implementations. One will handle the case when we have no value:
Another one will handle the case when we have value:
From the practical standpoint, any
Optional<T> in our application will always be an instance of either
None<T>, unless we add more implementations (sealed classes in Java 16+ solve this issue). In other words,
Optional<T> is a sum of types
How About Pattern Matching?
As mentioned above, algebraic data types and monads are concepts which are widely used in FP. In order to handle different cases (for example,
Some like shown above), Functional Programming uses pattern matching. In practice, it means that the received value is checked for type and then handled accordingly. For example, this is how such a situation is handled in Rust:
This looks clear, readable and convenient. The problem appears when we need to perform more than one operation on the returned value AND that operations also may return
Optional. Now we have something like that (let's continue using Rust-like syntax here):
As you can see, pattern matching works perfectly fine and compiler makes sure that programmer checks all possible cases every time. Needless to say, how good this for the code reliability. But writing and reading such a code is a pain.
Many FP languages adopted so-called
do syntax to make handling such cases much more concise and readable, but this is another (long) story and source of heated debates. Instead, I propose to look at another solution proposed by OO languages, in particular Java.
As you, probably, already noticed, in case of Java both classes implement the same interface. This means that we can just call instance methods without checking for particular type every time:
Adding more operations is just as easy:
It does not matter how many operations we need to compose together, code will remain concise, easy to read, write and maintain.
Of course, the curious observation shown above does not mean that one language/languages/paradigm/etc. is better than the other. Instead, I’m trying to show that we, as programmers, should have our minds open, as the best solution for the problem may come from an unexpected direction.
Originally published at https://dev.to on June 7, 2021.