
JEP 325: Switch Expressions - jcdavis
http://openjdk.java.net/jeps/325
======
joncampbelldev
I must have gone full lisp weenie, because I find it ridiculous when
remembering the days of being a happy java dev, waiting years for someone to
include a syntactic change in the spec, all whilst crossing my fingers that
they will include my use case.

Clojure macros have spoiled me with cond, condp and case plus whatever else I
or the community come up with. And my half thought out changes are nicely
isolated to a single project or library instead of baked into the language for
eternity.

~~~
runeks
Generally speaking, when a language developer adds really useful, special
syntax to a language — that language users cannot define themselves — it seems
like a good idea to stop, and consider whether it makes sense to restrict the
usefulness of this concept to just this special case.

I.e. the Java _boolean_ type, which can only be _true_ or _false_ (and not
_default_ ). Could it be that there are other valuable uses of this ability,
instead of using enums where you always have to handle the _default_ case?

Can it really be the case that _true_ / _false_ is the only case where not
having to worry about _default_ is extremely helpful?

~~~
joncampbelldev
I'm sorry, but I'm a little confused by your comment about default cases in
reply to mine about syntax extensions being in the hands of language designers
vs everyone.

I am also a little unsure what you mean by not worrying about default. Even
polymorphism has to deal with this, if you call a polymorphic method on an
object that doesn't implement that interface you'll get at best a compile
problem, at worst a cast exception. This is why some languages provide support
for a "default" or "method not implemented" hook in polymorphism.

If you are arguing that polymorphism is a better way to build systems than
switch/case statements then no argument here. Open constructs like
polymorphism are far better for growing and evolving large systems over time.

That doesn't mean we should exclude a nice way to say "i've got a variable
that i want to translate to some other value in a few different ways".

My point was only that it just seems so silly to me that everyone is
discussing whether this syntax is "best" or how it "doesn't do X, Y or Z",
because I remember doing that too before Rich Hickey (angelic choir noises)
showed me that actually you don't have to put up with that shit. Just use a
language that puts syntax extension in the hands of it's community and let
them figure it out without burdening the core language with a thousand little
tradeoffs for the 90% use case.

~~~
pjmlp
> Just use a language that puts syntax extension in the hands of it's
> community and let them figure it out without burdening the core language
> with a thousand little tradeoffs for the 90% use case.

The downside of that is that some Lisp projects can only be understood by the
original authors, during the time they were actually active on it.

I have seen a few that went DSL crazy with macros.

Scalaz is another "good" example of that.

------
pierrebai
My view is that this shows again that worse is better. The old form is better
because it is awkward and it can only be improved by turning it into a
function.

Within a function, each switch branch can be a return. Then you get the
benefits:

* Make the original function shorter.

* Give a descriptive name to what you're computing.

* Make the computation reusable.

It's what I find lacking with lambdas: it makes it too easy to forego good
refactoring because it makes too easy to write one-off code that in reality
will be rewritten many times in different places.

~~~
tigershark
No, worse is worse. Pattern matching is much, much cleaner than a switch with
the branches extracted in separate functions. I don’t think that anyone using
languages that support proper pattern matching can disagree.

If someone is writing the same lambda n times instead of extracting it in a
common method the problem is not the lambda function, but the developer.
Closures are probably the best feature in Java and they have been delayed
enough.

~~~
jjnoakes
> Pattern matching is much, much cleaner than a switch with the branches
> extracted in separate functions

I don't think that's what is being suggested. I think the suggestion is to
move the entire switch statement to a separate function, use 'return' in the
switch cases, and have the caller assign the variable once, from the return
value of the function.

------
on_and_off
Interesting to see Java copy modern languages good ideas.

The footgun of 'break' in switch statements has always bothered me, I strongly
prefer the kotlin/swift way of doing this.

~~~
bobbyi_settv
How is "break" in switch statements a footgun in Java? It is annoying
boilerplate to need a "break" after each case, but how can it lead to shooting
yourself in the foot?

~~~
on_and_off
Because I have seen more than once devs forgetting to add a 'break;',
resulting in more or less subtle bugs.

The kotlin/swift way of declaring this flow does not let you do this mistake.

~~~
m2ger
I recommend using ErrorProne in Your projects. There are several problems its
compiler will catch and fall through begin one of them:
[http://errorprone.info/bugpattern/FallThrough](http://errorprone.info/bugpattern/FallThrough)

It might take quite a lot of work to fix (or bypass) all the reported problems
in a large codebase but it is worth it.

------
based2
[https://reasonml.github.io/](https://reasonml.github.io/) \- When '|' becomes
a case.

[https://docs.scala-lang.org/tour/pattern-matching.html](https://docs.scala-
lang.org/tour/pattern-matching.html)

[https://antonioleiva.com/when-expression-
kotlin/](https://antonioleiva.com/when-expression-kotlin/)

[https://www.reddit.com/r/java/comments/7rgnei/jep_325_switch...](https://www.reddit.com/r/java/comments/7rgnei/jep_325_switch_expressions/)

------
ComputerGuru
I'm seeing a lot of interest in pattern matching as users that have discovered
it via rust's `match` or similar want to benefit from it in $dayjob language.

C# has seen a similar push to implement pattern matching, down to the use of
`_` as discard. The first iteration of this feature in C# 7 is feature light,
but it's a good start, and they've promised more to come. The team behind C#
has become really good at getting over NIH and embracing new technologies and
techniques while somehow preserving backwards compatibility and ergonomics.
Anders Hejlsberg doesn't kid around.

Here's an example cribbed from an official MSFT blog [0]:

    
    
        public static int Count<T>(this IEnumerable<T> e)
        {
            switch (e)
            {
                case ICollection<T> c: return c.Count;
                case IReadOnlyCollection<T> c: return c.Count;
                // Matches concurrent collections
                case IProducerConsumerCollection<T> pc: return pc.Count;
                // Matches if e is not null
                case IEnumerable<T> _: return e.Count();
                // Default case is handled when e is null
                default: return 0;
            }
        }
    

A lot of people have lamented this as the death of F#, but I think MSFT is
making the right calls here.

EDIT:

Little-known fact: C# actually _does_ have discriminated unions, and has had
for a long time (from the start?) in the form of the `Exception` base class.
In fact, pattern matching in C# can be "pulled off" (read: hacked together as
a proof-of-concept) without the changes from C# 7 by simply using the extended
catch syntax:

    
    
        class MyType1 : System.Exception
        { /* ... */ }
    
        class MyType2 : System.Exception
        { /* ... */ }
    
        void foo()
        {
            throw something ? new MyType1() : new MyType2();
        }
    
        void bar()
        {
            try
            {
                foo()
            }
            catch (MyType1 mtype)
            {
                //take type-specific action here
            }
            catch (MyType2 mtype)
            {
                //take type-specific action here
            }
            catch (Exception _)
            {
                //the _ was optional, but this is the `default` block
            }
        }
    
    
    

[0]:
[https://blogs.msdn.microsoft.com/seteplia/2017/10/16/dissect...](https://blogs.msdn.microsoft.com/seteplia/2017/10/16/dissecting-
the-pattern-matching-in-c-7/)

~~~
loopbit
I might be wrong, but I'd say that the interest in switch expressions and
pattern matching from the Java community most likely comes from Scala (a JVM
language) rather than Rust. Here's an example from the Scala docs:

    
    
      def showNotification(notification: Notification): String = {
        notification match {
          case Email(email, title, _) =>
            s"You got an email from $email with title: $title"
          case SMS(number, message) =>
            s"You got an SMS from $number! Message: $message"
          case VoiceRecording(name, link) =>
            s"you received a Voice Recording from $name! Click the link to hear it: $link"
        }
    }

~~~
dxxvi
No need for the '{' after "String = ". It'll make your code less crowded.

------
eecc
Doh, can’t we have pattern matching - with destructuring of course - and be
done with it?

(And tuples, ADTs ah the list goes on..)

~~~
dtech
There's a separate JEP [1] for pattern matching, and the switch expressions
are both preparation, companion and requirement for it.

[1]: [http://openjdk.java.net/jeps/305](http://openjdk.java.net/jeps/305)

~~~
eecc
Nice, I don't see how I could – at the very least - upvote for this proposal.
How does that work?

------
specialist
Assignable return values: +1

Concise AND expressions (eg "case UP, DOWN -> 1;"): +1

Using keyword 'break' instead of 'return': -1

~~~
mikelward
'break' feels ugly, but doesn't 'return' already have a different meaning
here?

You could do something context sensitive to make 'return' act differently in a
switch expression versus a switch statement, but that seems more confusing.

~~~
earenndil
> You could do something context sensitive to make 'return' act differently in
> a switch expression versus a switch statement

But you _shouldn 't_. That would break existing code. What if you actually
want to return something from within a switch statement?

~~~
mikelward
You ignored the parts where I said "already has a different meaning" and "that
seems more confusing".

------
blt
Glad to see conditional expressions becoming popular. TBH it's hard for me to
understand why anyone ever felt that switch/if should be statements. I guess
being too immersed in assembly programming?

------
stokito
I don't need this. In most cases everything can be solved with different
methods overrided by type. This is OOP way.

But it's makes language more complicated and what is more important - it makes
harder to refactor, especially via IDE. So my opinition: we don't need this
and this doesn't have any value besides marketing.

~~~
nine_k
The problem with OOP way is that it's overly wordy and complex in this case.
Much like switch statements in procedural code before the advent of OOP.

Type-based function overloads are not specific to OOP, by the way.

------
nikolay
This is inconsistent and no longer Java.

The -> is not a good idea and break <something> should be return <something>.
The -> throw e; "sugar" is just ridiculous!

~~~
SolaDev
IMO, return <something> may be ambiguous in switch expression.

~~~
nikolay
Yeah, but return gives the value of an expression and break is a control flow
keyword.

------
g7r
I want this in Go!

~~~
dtech
Don't count on it. Go decided against if-expressions despite them being much
more common, so I don't see pattern-matching-like construction coming to go.

------
joe_fishfish
This looks like a backport of Kotlin's `when` expression.

~~~
dtech
Is that a bad thing?

Also it's much more similar to Scala's "match" expression, which is also on
the JVM and predates Kotlin by about 7 years.

------
makecheck
Languages shouldn’t try so hard to wrap syntax around one _particular_ use for
something, because you end up tearing the whole thing apart in maintenance.
It’s sort of optimizing the writing of the code, which isn’t the worst part.

Here, “int result = switch...” seems nice for that one situation, “until it
isn’t” (and in my experience these “until” events can come as soon as the very
next time you have to maintain the thing). Oh, wait: I actually want to log
something in one of the cases in addition to the assignment but since the
entire thing is a fancy assignment expression now I have to rewrite 90% of it
to add a glorified print statement for one of them!?

And it’s not just Java. In “new” C++ for instance, the highly-specialized
syntax for loop iteration _is nice_ but it has the same problem: sometimes the
change you’re making _does need iterators_ , unusual increment points, etc.
and you end up peeling apart everything that was supposed to be so helpful and
rewriting way more than you should need to.

~~~
dtech
You're completely missing the point. The switch expression is exactly that, an
expression. It can be used wherever you can use an expression. This is
fundamental to programming languages and especially in functional programming
languages where it is a requirement of everything in the language.

You can use logging/side effect in it. You can not assign it to a variable,
all valid in Java.

Consider the if expression (ternary ?: operator in Java). In most modern
languages (e.g. Scala, Kotlin, Rust) there is no ternary operator, and only an
if-expression:

    
    
        int value = if(something) 1 else 2 
    

According to you, this is wrong, but it isn't, you can still use it as a
statement:

    
    
        if(something) {
           doThis()
        } else {
           doThat()
        }
    

Just like you can have a statement (line) with just "1+1" or "doThis()", you
can have a statement of just an if-expression.

And you can also add side-effect (logging) withing expressions:

    
    
        int value = if(something) {
            log("Uh oh");
            -1
        } else {
            1
        }
    

Making a language construct an expression is the complete opposite of tying it
to a particular use-case, it's giving it the ultimate flexibility because you
can use expressions _everywhere_.

~~~
makecheck
I’m not debating why expressions are part of languages. I’m saying that I
shouldn’t have to tear apart half the code just to make a change.

And there are very simple examples of that problem, right in the article:

1\. Allowing the entire switch expression to bind to the same assignment makes
it difficult to maintain when adding a case that _will not_ perform that
assignment. Now, instead of adding one line, you’re tearing apart half the
block and trying to find a way to make equivalent new code.

2\. Even adding a single line to a case may be impossible without rewriting
the case. Given some 'case "Bar" -> 2;', the maintainer has to redo the entire
thing: rewrite it as 'case "Bar":' with a colon, add 'break 2;' to preserve
original behavior, and _then_ add their stuff. That’s just broken; it won’t
even produce a clean "diff" because you’re “changing” lines that don’t have
anything to do with your new code. And you can bet at some point that somebody
may just plain forget to add back the "break" when rewriting the expression
needlessly, introducing a bug.

