
Pattern Matching for Java - steve_barham
http://cr.openjdk.java.net/~briangoetz/amber/pattern-match.html
======
peeters
IMO this is the biggest thing available to modern languages that Java is
missing. I would absolutely love to see this, particularly pattern
decomposition. I wonder if you could do it without something analogous to
Scala's sealed classes though--you really want your type checker to be able to
assert every match has considered every branch (without having to specify a
"default" everywhere). That means you need to be able to mark classes as not-
dynamically-extendible, so the type checker has the full set of subtypes
available.

Edit: Just got to the bottom of the article. Looks like sealed hierarchies is
exactly what they explore.

~~~
mark242
I mean, reading this article is basically a giant advertisement for Scala and
its powerful pattern matching.

    
    
      functionThatReturnsAnyVal match {
        case i: Int if i > 2 => System.out.println("Greater than 2")
        case i: Int => System.out.println("Less than or equal to 2")
        case s: String => System.out.println("Hello, " + s)
        case _ =>
      }

~~~
taeric
This still seems a terrible example just to try and avoid naming an object.
Isn't the comparison to:

    
    
        Object o = functionThatReturnsAnyVal();
        if (o instanceof Integer) {
            Integer i = (Integer) o;
            if (i > 2) {
               System.out.println("Greater than 2.");
            } else {
               System.out.println("Less than or equal to 2.");
            }
         } else if (o instanceof String) {
            String s = (String) s;
            System.out.println("Hello, " + s);
         }
    

I get that the case syntax is kinda nice, but this particular example just
doesn't seem to get there for me. Roughly half the lines, which is good. None
of them hard to reason about. Which makes it a wash.

Or is the comparison to something else?

~~~
mark242
In reality my function would rarely exist in the Scala world since having a
weakly-typed return like that is cringeworthy to say the least. But it's
better to see this in action with Scala options.

    
    
      val myOptionalVar: Option[String] = functionThatReturnsOption()
    
      myOptionalVar match {
        case Some(str) if str.length > 10 => System.out.println("This is a long string.")
        case Some(str) => System.out.println("This is a short string.")
        case None => System.out.println("This code would be fifteen lines of null checking in Java.")
      }

~~~
pvorb
With Java 8 having Optional and Lambdas, this is no longer the case:

    
    
      final Optional<String> myOptionalVar = functionThatReturnsOptional();
      
      final String output = myOptionalVar
        .map(str -> str.length() > 10
          ? "This is a long string."
          : "This is a short string.")
        .orElse("This code would be fifteen lines of null checking in Java.");
      
      System.out.println(output);

------
lacampbell
Why the fixation on pattern matching? Don't get me wrong, I enjoy the benefit
it provides, but for OO code multiple dispatch is a more elegant and idiomatic
way to solve the "I don't want to implement the visitor pattern" problem.

FYI, I recently found out that C# can actually do multiple dispatch

[https://blogs.msdn.microsoft.com/shawnhar/2011/04/05/visitor...](https://blogs.msdn.microsoft.com/shawnhar/2011/04/05/visitor-
and-multiple-dispatch-via-c-dynamic/)

~~~
alkonaut
To me it seems pattern matching without possibility to actually enforce that
all cases are matched seems to miss the most useful scenario:

What I want to do is create proper sum types. For some reason most OO
languages were designed around the idea that being "open for extension" was a
good thing. Normally if I make a base class, I want a _closed_ and _known_ set
of sub classes. E.g. if I make a payment method then I want to make a Credit
or Invoice kind where one requires Credit Card details and the other requires
an address. In F# that's a simple sum type. In C# making a sum type is a
convoluted mess of making an outer base class and private nested sub classes.

Here a sum type with the two cases plus an _exhaustive_ pattern matching
switch would have solved in 10 lines what takes me 300 lines to do in C#. I
think the concept of "fixed" or "closed" hierarchies is foreign enough to OO
that it probably needs to be a completely separate concept from the normal
types. F# shows that it can be implemented as sugar on top of the regular CLR
type system. All that's needed is that some keyword such as "enum class" is
converted into a sealed hierarchy of plain record classes with no methods on
them. The switch statement we already have can then treat these "enum classes"
specially by checking that all cases are matched.

A sketch solution for C# could look like

    
    
        enum class PaymentMethod
        {
            CreditCard(CrediCardDetails cc),
            Invoice(Address billingAddress),          
        }
        
        // later
        switch (paymentMethod)
        {
          case CreditCard(ccdetails):
             Console.WriteLine($"Creditcard expiring {ccdetails.ExpiryDate}");
          case Invoice(addr):
             Console.WriteLine($"Bill to {addr.Name}");
        }
    

Writing the above in current C# would require significantly more boilerplate.
This isn't the best pattern to use in all situations: but in many cases when
the types are merely data carriers it doesn't make sense to put the logic _in_
the class as OO prescribes, so the FP recipe works much better.

~~~
lacampbell
In the C# example in the code I posted, all cases are matched. If you add a
new animal subclass, that's not "explicitly" handled, it will route to the
default (Animal, Animal) method.

It's equivalent of

    
    
        _ -> default_function
    

At the end of a pattern matching statement.

Essentially it's _impossible_ for not all the cases to be matched, as far as I
can see.

~~~
alkonaut
Yes but making new classes map to default is what a default clause already
does (which is basically what a base case in an abstract base class does with
normal inheritance).

What I want is to make a new type added mean the code won't compile until it
is _explicitly_ handled in all pattern matches that do not have a default
clause. Basically what I'm saying is that this:

    
    
       bool HandlePayment(PaymentMethod pm) {
          switch(pm) {
             case Invoice(addr):
                  SendInvoice(addr); 
                  return true;
             case CreditCard(ccdetails)
                  return PrcessCreditcard(ccdetails);
             default:
                  // What?
          }
       }
    

Would be a lot better if it didn't require the default, and the compiler could
detect the error that occurs when someone adds a new case (BitCoin) to the
types of payment method. Inheritance and abstract baseclass for the processing
means that you'd do this

    
    
       bool HandlePayment(PaymentMethod pm)
       {
          return pm.Process(order);  // sends an invoice, charges credit card etc
       }
    
       and you simply have 
    
       abstract class PaymentMethod { abstract bool Process(order); }
       class CreditCard : PaymentMethod {  ... }
       class Invoice : PaymentMethod {  ... }
    

And this is of course the regular way of writing OO, but I find it unnatural
and cumbersome in many cases. It's hard to define concise descriptions of what
types are actually available, and you have to write a ton of boilerplate to
ensure a closed hierarchy and complete matching in all scenarios where you
can't enclose the logic inside each type.

------
tybit
I was originally very happy to hear pattern matching was coming to C#, but I
don't think it is actually that useful a feature without discriminated
unions/sum types too.

~~~
platz
What C# has now is better described as fancy form of Destructuring . Perhaps
they'll add actual pattern matching in a later release.

Actually the proposed java version is better than C#'s because it allows a
form of exhaustiveness checking which C# doesn't even attempt to provide for.

However they had to add the `sealed` keyword too, which does not currently
exist in java

------
kodablah
"How we declare the destructuring pattern in the AddNode class, or declare
AddNode so that it implicitly acquires one, will be covered in a separate
document."

Please use a Scala unapply() and not just constructor parameters. The former
gives much more latitude to build patterns.

I have a ton more to say about all of this having written in Scala and Kotlin
extensively. I'll just say that it is useful they are already thinking about
new variable assignment to parts of matches and NOT concerning themselves w/
"smart casts".

------
raspasov
Pattern matching is possibly one of the most under-utilized approaches that
can simplify a lot of ugly/complex logic.

~~~
zerr
On the other hand it "prettifies" bad code style - branching by dynamic casts
/ type comparisons.

~~~
raspasov
I disagree. What's bad about that/what's the alternative?

~~~
lmm
The problem with pattern matching as I've seen it implemented is that unsafe
and safe pattern matching look exactly the same (even the exact same code line
can be safe or unsafe depending on context). See the bottom of
[http://typelevel.org/blog/2014/11/10/why_is_adt_pattern_matc...](http://typelevel.org/blog/2014/11/10/why_is_adt_pattern_matching_allowed.html)
. For this reason I tend to prefer explicit virtual methods even though
they're more cumbersome.

~~~
raspasov
The pattern matching I have in mind is very dynamic in nature and does not
concern itself at all with some static notion the data that is being matched.
Either there's a match, or there isn't. It's a very binary outcome.

~~~
lmm
That's not the important/valuable use case for what people normally refer to
as "pattern matching".

~~~
raspasov
I've recently found dynamic pattern matching pretty valuable to completely
replace REST-like server-side APIs with a data-oriented API that gets pattern
matched and dispatched based on the actual values and shapes of the data
structure.

It helps almost completely avoid the /get/this /get/that /set/those explosion
of API getters and setters that ultimately leads to very complex client logic
that makes any notion of consistency at a distance very hard to reason about.

~~~
lmm
I really don't think you're talking about pattern matching in the usual sense.
So it's probably better to use a different term.

------
evdev
IMO the hierarchy of need for this goes:

1 - case classes / value classes / data classes, whatever you want to call
them.

2 - match-and-bind syntax

...

11? - fancy pattern matching

This maybe says more about how much I pay attention to what's upstream in
Java, but I found the fact that this is just a hypothetical proposal, in April
of 2017, strangely shocking. I guess I figured it _had_ to be on the docket
for a future java version already.

~~~
aardvark179
This is not just a hypothetical proposal, I believe there is a working
prototype and a lot of thought has gone into this. More things are coming, but
pattern matching is a low hanging fruit that doesn't have difficult
interactions with the type system or VM. Value classes need a lot of language
enhancement to be really useful, and those things are likely to land before
value classes themselves.

~~~
pdpi
Project Valhalla (featuring value classes) is scheduled for Java 10 — so it'll
probably arrive in production around 2020.

~~~
aardvark179
I cannot comment on the Java release schedule, but Project Valhalla will be
done when it's done, and it is not a small task. Project Amber is intended for
smaller changes that may have been investigated as part of Valhalla and can be
released earlier.

------
cromwellian
To me the biggest missing features are not this but:

* heredoc/multiline string/embedded interpolated strings * concise array, map, and object literal initializers. * structural/anonymous types

Simple things like multivalue return become an exercise in boilerplate in
Java. As much as I like Immutables.org or @AutoValue, I should be able to
return a struct or use destructuring operations at the callsite.

The hack in Java is to use annotation processors to provide nice fluent
builder patterns, but really the language should have first class support for
this.

~~~
sgift
> concise array, map, and object literal initializers.

[http://openjdk.java.net/jeps/269](http://openjdk.java.net/jeps/269) Just a
few more month.

Still nothing for the other two, but 1/3 is better than nothing.

~~~
cromwellian
Those aren't new language features though, mostly just standardizing Guava
conventions into the JDK.

~~~
sgift
Fair enough, but they do the job. I understand the wish of the designers to
not change the syntax if a standard library extension does the job for 99% of
the cases. Changing the language syntax is a far more elaborate process.

~~~
cromwellian
I think it gets real ugly once you need nested data structures, the
readability goes down (maps of lists of maps). Also, using overloads means
it's limited to small arity, e.g. 10.

Not having first class multiline literals really makes it painful to write
something like React or GraphQL in Java.

One of the things I give props to MS for is having the courage to evolve C#
and TypeScript at a breakneck pace. Each release of TS, they add features I've
been wishing for. They seem to be listening to developer productivity issues
and responding.

I under Java's desire to preserve backwards compatibility, but they end up
breaking it anyway, and make design decisions based on how they can shoehorn
things into the older JVMs, but each new release ends up with some issues.
Witness how long it has taken folks to migrate to Java8. If we're going to
wait years for major releases, then let's make them well worth the trouble
when they finally arrive.

~~~
sgift
> Not having first class multiline literals really makes it painful to write
> something like React or GraphQL in Java.

Certainly. And that's one of my biggest pain points with Java, but a different
point in your list. One day they will exist ... one day. Hopefully.

> Witness how long it has taken folks to migrate to Java8. If we're going to
> wait years for major releases, then let's make them well worth the trouble
> when they finally arrive.

I've used every release since JDK 6 in production one or two month after it
was released. Once that hurt me (Rhino/Nashorn), but usually it hasn't been a
problem. I think people who wait until the last moment when the older versions
go out of support to switch JDKs do themselves a disservice out of fear. And
fear is never a good reason.

------
rattray
I'm currently thinking through the design of a pattern-matching feature for
JavaScript, through a superset I'm building called LightScript[0].

I liked that this article laid out the specific options and various shades of
gray that can constitute parts of "pattern matching".

I'm curious for thoughts on a syntax like this:

    
    
        x = match y:
          case > 3: () => "it's bigger than three"
          case 2: () => "it's strictly equal to two"
          case Integer: (x) => `some int smaller than two: ${x}`
          case String: (s) => `some string: ${s}`
          case Array: ([ first, ...rest ]) => `a list starting with ${first}`
          case Object: ({ a, b }) => `an object with property a: ${a}`
          

This is somewhat more difficult given that even when using Flow or TypeScript,
there's relatively little type granularity available at runtime.

Any thoughts?

[0] [http://lightscript.org](http://lightscript.org)

------
rdnetto
Would be really great to see this make it's way into the language. Here are
some ways I've found to work around its absence: * adding a match() function
to a common base type. So if it could have subtypes Foo(x), Bar(y, z), then
the signature would look like:

    
    
      <T> T match(Function<X, T> foo, BiFunction<Y, Z, T> bar);
    

* in the cases where I don't have control over the common base type, I've written a builder pattern that constructs a sequence of predicate-function pairs and applies them appropriately. This looks like so:
    
    
        new PatternMatchingHelper()
        .append(Foo.class, foo -> doSomething(foo.x))
        .append(Bar.class, bar -> doSomethingElse(bar.y, bar.z))
        .otherwise(x -> fallbackValue())
        .apply(objectOfUnknownType);
    

The main disadvantage here is that it doesn't work well with generics, because
the class objects have raw types.

You could probably extend the second approach to get something close to the
arbitrary predicates that pattern matching would provide, but the syntax
wouldn't be nearly a clean as having it in the language.

~~~
jnordwick
Just use another language if you want pattern matching so badly. Twisting Java
to look like Scala doesn't do the language justice.

~~~
RhodesianHunter
You act as if what op described must be the entirety of their code base. What
if pattern matching is just a small part of a larger problem better solved
with Java?

------
flavor8
Any substantial differences from Scala's implementation? From what I remember
of Odersky's book it seems very similar if not identical. (Which is fine - I'm
all for Java incorporating the best parts of Scala.)

~~~
kelnos
The syntax looks similar, but there's no mention of an `unapply()`-type thing,
which would limit the flexibility of this proposal greatly.

------
tannhaeuser
If you like pattern matching, you'll _love_ it's generalization into Prolog
unification as explained in eg. [1].

[1]:
[http://www.amzi.com/AdventureInProlog/a10unif.php](http://www.amzi.com/AdventureInProlog/a10unif.php)

------
nightmunnas
I really really like these suggestions. Especially the first parts where I
have found myself writing a lot of instance-of-cast and just marvel at how
much clutter it actually adds without adding more description to the code. I
do also ponder if there is a limit to how much we should potentially shorten
the expressions, not to make the expressions extremely dense it understanding.
The last opinion is just taste though, admittedly.

------
dkarl
I feel these new language features that get grafted onto Java end up awkward
and unpleasant to use. Java is still Java, and it exacts a stiff awkwardness
tax for writing code in a style different from how Java OO was envisioned
15-20 years ago. The examples in the linked article look fine, but the
difference between pattern matching as "possible direction" and pattern
matching as "new language feature" could easily end up like the difference
between Orson Welles as Charles Foster Kane and Orson Welles as Falstaff.

I wonder if Oracle could create some excitement around the platform by
creating or adopting a new language as an official repla^D^D^D^D^D "new member
of the family" to implicitly succeed Java just like C# replaced Visual Basic
for most use cases. Java could be kept around as a sop to die-hards like
VB.NET was. It's great that the JVM allows a thousand flowers to bloom, but
it's not great that the only "official" choice on the JVM is a language that
hasn't been able to evolve very far from its 1990s roots.

~~~
jeremyjh
Oracle's most profitable customers are still using Java 6. That wouldn't
change if they were pushing Kotlin.

~~~
dkarl
I don't know Kotlin, but judging by their "Kotlin for Scala Developers"
article I don't know if it would be a big enough jump to get people excited,
especially since a project like this would take years to come to fruition. I
think Scala is a good first try at what the successor language could be, but
it has two problems: its reputation as a hard language, and the fact that it
partly deserves said reputation. (Maybe if Odersky succeeds in simplifying the
concepts underlying Scala as he's trying to now with Dotty, the result will be
a potential Java-killer.) Oracle would have to fund a research project to
choose an existing candidate and spend years developing it to fit the role it
would play in the market. Which sounds very unlike Oracle, so I'm not holding
my breath.

EDIT: And to your point, I think it's a bad sign if their most profitable
customers think that none of the Java language updates they've released in the
last ten years is worth upgrading for. The longer their customers stick with
2006-era Java, the longer they pass up Oracle's current offerings, the less
attached they feel to the future of the platform, and the more likely they are
to make a big change when they do make a change.

------
megawatthours
You can do this in Java using derive4j:
[https://github.com/derive4j/derive4j](https://github.com/derive4j/derive4j).

It generates code for algebraic data types which offer a typesafe interface
similar to the `case` statements in languages that natively support pattern
matching.

~~~
Latty
In the same way you could "do" lambdas with single method interfaces in Java
pre-8. It was possible, but it was still a pain to do.

~~~
jbgi
It depends. Bare minimum, in scala you have to do:

    
    
      sealed trait Either[A, B]
      final case class Left[A, B](a: A) extends Either[A, B]
      final case class Right[A, B](b: B) extends Either[A, B]
    

and it is still not a real sum type due to exposing subtyping (cf.
[https://github.com/quasar-analytics/quasar/wiki/Faking-
Sum-T...](https://github.com/quasar-analytics/quasar/wiki/Faking-Sum-Types))

with derive4j:

    
    
      @Data
      interface Either<A, B> {
        <X> X match(Function<A, X> left, Function<B, X> right);
      }
    

or, at your preference:

    
    
      @Data
      interface Either<A, B> {
        interface Cases<X> {
          X left(A leftValue);
          X right(B rightValue);
        }
        <X> X match(Cases<X> cases);
      }
    

So it is actually not much boilerplate.

The real drawback of (any) java implementation is lack of TCO.

------
stickfigure
The syntax still looks tedious compared to Ceylon:

    
    
        if (is String name) {
            // compiler knows 'name' is String in this block
            print(name);
        }
        else {
            print("some other text");
        }
    

Why declare a new variable? The Ceylon syntax works great with union types
too.

~~~
jdmichal
I had the same comment on C#'s implementation:

[https://news.ycombinator.com/item?id=12971841#12972691](https://news.ycombinator.com/item?id=12971841#12972691)

In C#, the "wanting the supertype" answer makes some sense with explicit
interface implementations [0]. Java doesn't have that, and AFAIK there's no
way to declare something that's visible on a supertype but not a subtype.

[0] I think it's a bad reason. But the possibility does at least exist.

------
cestith
How about...

    
    
        instanceOf x is? {
            Int { System.out.println("It's an Int"); }
            String { System.out.println("Hello, " + x); }
            _default { System.out.println("This type is not explicitly named here."); }
        }
    
    

And if you don't want the type, the value of any other method, function, or
attribute could be checked by "is?" or whatever syntax token you want there.

Further tests could be saved for within those blocks to save complexity.

What's odd though is that in Java this particular example seems to want multi-
method. Testing the type explicitly and acting on it is more akin to duck-
typed language programming. You see this pattern pretty often in Perl for
example. If I want to write in Perl, I typically reach for Perl.

~~~
djhworld
It's not just the types/instanceof operator though

They're talking about making the Pattern matcher match more complex patterns.

------
scadge
Along with with Value Types [1] Java 10 could be an even more significant
improvement than Java 8 was :)

[1]
[http://cr.openjdk.java.net/~jrose/values/values-0.html](http://cr.openjdk.java.net/~jrose/values/values-0.html)

------
gjmacd
Jeez, seems like overkill when you get that free in more modern of stacks.
Move to Elixir. Pattern matching is done right and it's brilliantly designed.
I understand that if you're having to maintain Java applications, but why
anybody would stomach a NEW project on that behemoth is beyond me. I'm not
just talking about performance, I'm talking about object topology BS,
complicated libraries, etc. Java (for me now) seems like it's bolted on to the
Web and simply doesn't have it's place as a Web stack. My opinion of course
but with the great amount of stacks out there -- I don't get the fascination.

------
idsout
I would love to see this land in Java. Pattern matching is one of my favorite
features of Rust.

------
thinkmoore
There is some very cool language design work from Chinawat Isradisaikul and
Andrew C. Myers at Cornell on adding very powerful matching constructs to a
Java-like language.

Paper:
[http://www.cs.cornell.edu/~chinawat/papers/pldi13-p343-israd...](http://www.cs.cornell.edu/~chinawat/papers/pldi13-p343-isradisaikul.pdf)
Slides: [http://www.cs.cornell.edu/~chinawat/papers/chin-
pldi13-slide...](http://www.cs.cornell.edu/~chinawat/papers/chin-
pldi13-slides.pdf)

------
zastrowm
Reading this reminded me of all the discussions that took/are taking place for
pattern matching in C#[0]; reading this I had the same reactions that I did
for C#: cool stuff. It's also nice to see the languages reach parity in
features; I only hope the two language committees pay attention to the
research/feedback that the other receives.

[0]:
[https://github.com/dotnet/roslyn/issues/10153](https://github.com/dotnet/roslyn/issues/10153)

------
seanalltogether
"Finally, the above code is less optimizable; absent compiler heroics, it will
have O(n) time complexity, even though the underlying problem is often O(1)."

Is this addressed with pattern matching?

------
rbjorklin
How does Vavr's (formerly Javaslang) pattern matching hold up?
[http://www.vavr.io/](http://www.vavr.io/)

~~~
hepta
I already use vavr, sadly such pattern matching is not exhaustive, which is
the most important feature for me.

------
legulere
Java already has simple pattern matching with exceptions (try {throw ...}
catch ...). It's surprisingly ergonomic, even though it's just a hack

------
kahnjw
Shameless plug: I recently wrote a similar piece exploring pattern matching in
Javascript
[http://jarrodkahn.com/posts/6/slug/](http://jarrodkahn.com/posts/6/slug/)

------
cschep
Looks quite Swift-like. Seems like a big win all around for pattern matching
to become more "main stream".

------
kpil
This is nice. But I think my number one feature would be first-class
relationships in object oriented languages.

------
leifg
Am I missing something or can most of the examples just be achieved with
method overloading?

~~~
ysleepy
Java only has single dynamic dispatch. That means it will use the method of
the most specific sub-class, but will not dispatch on the runtime type of the
argument, only its compile time type.

So something like:

    
    
      for(None n: expreTree.children()) {
        intMath.eval(n);
      }
    

will not work as expected if eval is overloaded.

------
guelo
Really great proposal. Is this conversation in relation to Java 10 or just pie
in the sky?

~~~
dasmoth
_This is an exploratory document only and does not constitute a plan for any
specific feature in any specific version of the Java Language._

I hope it does go forward, though. Really useful feature, and given the
constraints of the existing language, the proposed syntax looks pretty
liveable-with.

------
chvid
I honestly don't need this feature.

Am I really the only one?

Or do you all just have buckets of source code filled with if instanceof then
cast just screaming for this language feature?

~~~
kazagistar
I have buckets of code that would be much shorter and more typesafe if it was
that way at least.

------
loukrazy
ITT at least 3 JavaScript libraries to do this.

Also how the heck do you destructure Java objects with private fields without
resorting to bean accessors?

------
siddharthbhola
good approach taken from scala programming language. makes things simpler and
shorter.

~~~
ysleepy
It is an old concept used in most functional languages, like ML, haskell or
even Prolog via unification.

So not an invention of scala, only that is also lives on the JVM.

[http://wiki.c2.com/?PatternMatching](http://wiki.c2.com/?PatternMatching)

------
nikolay
Really?! exprswitch?! Unreadable and long!

~~~
guelo
That's just a placeholder. Several times in the doc they say that the operator
name needs to be explored, specifically to see if overloading 'switch' is
feasible.

------
jiaweihli
If you're interested in exploring pattern matching, I've created a
Javascript/TypeScript library dedicated to it. [0]

You can see it live! [1]

[0]
[https://github.com/jiaweihli/rematch](https://github.com/jiaweihli/rematch)

[1]
[https://runkit.com/jiaweihli/57db70d841de7f1400d64f73](https://runkit.com/jiaweihli/57db70d841de7f1400d64f73)

