Will Java 8 support pattern matching like Scala and other functional programs do? I'm putting a presentation together of Java 8's Lambda features. I can't find anything on this particular Functional-programming concept.
I remember what got me interested in functional programming was the quicksort implementation, especially compared to imperative programming's implementation.
From Java 16 onwards, we can do pattern matching using the instanceof operator. It can be used on if statements, and we can use it as type guards on variable assignments and returns. This lets us use a variable without further casting after a type check. This is a new syntax added to the language.
Thus, the term pattern matching in Java means matching a regular expression (pattern) against a text using Java. The Java Pattern class can be used in two ways. You can use the Pattern. matches() method to quickly check if a text (String) matches a given regular expression.
Regular Expressions or Regex (in short) in Java is an API for defining String patterns that can be used for searching, manipulating, and editing a string in Java.
I suppose you are not talking about pattern matching in the sense of applying a regular expression on a string, but as applied in Haskell. For instance using wildcards:
head (x:_) = x tail (_:xs) = xs
Java 8 will not support that natively, with Lambda expression there are, however, ways to do so, like this for computing the factorial:
public static int fact(int n) { return ((Integer) new PatternMatching( inCaseOf(0, _ -> 1), otherwise( _ -> n * fact(n - 1)) ).matchFor(n)); }
How to implement that you will find more information in this blog post: Towards Pattern Matching in Java.
It's possible to implement pattern matching as a library in Java 8 (taking advantage of lambda expressions), but unfortunately we will still be missing the compiler exhaustiveness check that languages such as Haskell or Scala have.
Cyclops-react has a powerful Pattern Matching module, which offers both structural pattern matching for Java 8, and pattern matching via guards.
It provides a when / then / otherwise DSL and matching, including deconstruction is based on standard Java Predicates (so matching can be used to filter a Stream for example).
For matching via guards we use whenGuard / then / otherwise to clearly show the case is driving the test and not the structure of the Object under test.
e.g. For guard based matching, If we implement a Case class that implements the Matchable interface
static class MyCase implements Matchable{ int a; int b; int c;}
(btw, Lombok can come in very handy for creating sealed case class hierarchies)
We can match on it's internal values (recursively if neccessary, or by type among various other options).
import static com.aol.cyclops.control.Matchable.otherwise; import static com.aol.cyclops.control.Matchable.whenGuard; new MyCase(1,2,3).matches(c->c.is(whenGuard(1,2,3)).then("hello"), .is(whenGuard(4,5,6)).then("goodbye") ,otherwise("goodbye") );
If we have an Object that doesn't implement [Matchable][3], we can coerce it to Matchable anyway, our code would become
Matchable.ofDecomposable(()->new MyCase(1,2,3))) .matches(c->c.is(whenGuard(1,2,3)).then("hello"), .is(whenGuard(4,5,6)).then("goodbye") ,otherwise("hello"));
If we don't care about one of the values we can use wildcards
new MyCase(1,2,3).matches(c->c.is(whenGuard(1,__,3)).then("hello"), .is(whenGuard(4,__,6)).then("goodbye") ,otherwise("hello) );
Or recursively de-structure a nested set of classes
Matchable.of(new NestedCase(1,2,new NestedCase(3,4,null))) .matches(c->c.is(whenGuard(1,__,has(3,4,__)).then("2") ,otherwise("default");
Where NestedCase looks something like this -
class NestedCase implemends Decomposable { int a; int b; NestedCase c; }
Users can also compose pattern matching expressions using hamcrest
import static com.aol.cyclops.control.Matchable.otherwise; import static com.aol.cyclops.control.Matchable.then; import static com.aol.cyclops.control.Matchable.when; Matchable.of(Arrays.asList(1,2,3)) .matches(c->c.is(when(equalTo(1),any(Integer.class),equalTo(4))) .then("2"),otherwise("default"));
We can also match on the exact structure of the Object being tested. That is rather than use if / then tests to see if the structure happens to match our cases, we can have the compiler ensure that our cases match the structure of the provided Objects. The DSL to do this is almost identical as per guard based matching, but we use when / then / otherwise to clearly show the Objects structure drives the test cases and not vice versa.
import static com.aol.cyclops.control.Matchable.otherwise; import static com.aol.cyclops.control.Matchable.then; import static com.aol.cyclops.control.Matchable.when; String result = new Customer("test",new Address(10,"hello","my city")) .match() .on$_2() .matches(c->c.is(when(decons(when(10,"hello","my city"))),then("hello")), otherwise("miss")).get(); //"hello"
Structurally matching on an Address Object extracted from a customer. Where the Customer and Address classes look this this
@AllArgsConstructor static class Address{ int house; String street; String city; public MTuple3<Integer,String,String> match(){ return Matchable.from(()->house,()->street,()->city); } } @AllArgsConstructor static class Customer{ String name; Address address; public MTuple2<String,MTuple3<Integer,String,String>> match(){ return Matchable.from(()->name,()->Maybe.ofNullable(address).map(a->a.match()).orElseGet(()->null)); } }
cyclops-react provides a Matchables class which allows structural pattern matching against common JDK types.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With