Extending when clauses beyond pattern matching for switch
Dimitris Paltatzidis
dcrystalmails at gmail.com
Wed Apr 20 20:23:26 UTC 2022
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-spec-comments
mailing list