Fwd: Extending when clauses beyond pattern matching for switch

Brian Goetz brian.goetz at oracle.com
Thu Apr 21 16:54:42 UTC 2022


This came in on the amber-spec-comments list.

The question is a special case of one we've discussed before, which is 
"could we relax the arity of switches".  Switches currently have exactly 
one operand, but the semantics extends naturally enough both to zero and 
more than one.  As a silly example of where more than one operand might 
be useful, consider this version of "FizzBuzz":

     int m5 = i % 5;
     int m3 = i % 3;
     String fizzbuzz = switch (m5, m3) {
         case (0, 0) -> "FizzBuzz;
         case (0, _) -> "Fizz";
         case (_, 0) -> "Buzz";
         default -> String.valueOf(i);
     }

(Languages with tuples and pattern matching, such as ML, get this for 
free; Java would have to make a small step to get there.)

The examples given in the original message explore whether nilary 
switches are useful (answer: potentially, this was mentioned in one of 
the earlier writeups), and the addition of `when` clauses paints the way 
to a more concrete representation for a multi-way conditional (please, 
no syntax comments):

     String s = switch {
         case when e1 -> "a";
         case when e2 -> "b";
         default -> "c";
     }

So both of these forms are clearly "useful", though the bar for "so 
let's put it in the language" is way higher than that.  Suffice it to 
say that these are things we have considered, and might consider more 
seriously someday, but are not in a super hurry about.


-------- Forwarded Message --------
Subject: 	Extending when clauses beyond pattern matching for switch
Date: 	Wed, 20 Apr 2022 23:23:26 +0300
From: 	Dimitris Paltatzidis <dcrystalmails at gmail.com>
To: 	amber-spec-comments at openjdk.java.net



Pattern matching is asking questions about what something is. "when"
clauses complement it, so we can fine tune:

Collection<String> c = ...
String r = switch (c) {
case List<String> l when l.isEmpty() -> "empty list";
case List<String> l when l.size() > 10 -> "big list";
case List<String> l when l.contains("test") -> "it does";
default -> "none";
};

But, what if we already know the answer to our question? If case's' are
asking "what is it?" then when's' are asking "how it behaves?"

List<String> l = ...
String r = switch (l) {
when l.isEmpty() -> "empty list";
when l.size() > 10 -> "big list";
when l.contains("test") -> "it does";
default -> "none";
};

That is, making a distinction of when switching over a type against when
switching over a value.
Effectively, we already have behavioral switches:

int a = ...
switch (a) {
case 1 -> ...
case 2 -> ...
default -> ...
}

Even though somewhat ambiguous, we can say that the above switch is not
asking what a is (here "is" refers to the type, not the value), but how it
behaves. Patterns are type-based, while behaviours are value-based.

Of course, we are all aware of the range checking situation in a
switch, that currently no construct supports it. This is not about solely
solving it, but a behavioral generalization to all types.

A few questions are raised:
- Totality: In a behavioral switch, it's nearly impossible if not to reason
about it. This effectively pushes users to a permanent boilerplate default
clause.
- Dominance: More or less this is already solved with the current guards.
- Purity: Can we have a mixed pattern and behavioral switch at the same
time? Is it even meaningful as a general construct?

// purity example - does it even make sense?
Collection<String> c = ...
String r = switch (c) {
when c.isEmpty() -> "empty collection"; //switching over value
case Set<String> s when s.size() > 10 -> "big set" //first over type
then over value
case Set<String> s -> "a set" //switching over type
default -> "none";
};

The above raises the science fiction question:
// Looks mostly as a no-no, but in the context of this behavioural
generalization, could it stand?
// We filter first on the behaviour and after that on the type
when c.isEmpty() case Set<String> s -> "empty set";

Yes, this could be an attack on the ill-used long-ish if-else-ifs.
Nevertheless, the gravity is towards a philosophical take on what patterns,
guards and switches are, could be and what are the problems they tackle.
Personally, I don't know how I feel about them (these non-existent solely
"when" switches). They certainly could add a lot of complexity into the
language that is probably unjustifiable.


More information about the amber-dev mailing list