On 7/23/2020 11:52 AM, Brian Goetz wrote:
On 7/23/2020 2:38 PM, Remi Forax wrote:
On guards, they do not work well with exhaustiveness. Still not sure
they are useful.
It works fine, it's just more work for the user to get right.
We induce a domination ordering on patterns. If `T <: U`, then `T t` <
`U u` (`T t` is less specific than `U u`.) Similarly, for all guard
conditions g, `P & g` < `P`. What this says is that if you want
exhaustiveness, you need an unguarded pattern somewhere, either:
case A:
case B & g:
case B: // catches B & !g
case C:
or
case A:
case B & g:
case C:
case Object: // catches B & !g
I understand your diffidence about guards, but I'm not sure we can do
nothing. The main reason that some sort of guards feel like a forced
move (could be an imperative guard, like `continue`, but I don't think
anyone would be happy with that) is that the fall-off-the-cliff behavior
is so bad. If you have a 26-arm switch, and you want the equivalent of
the second of the above cases -- B-but-not-g gets shunted into the
bottom clause -- you may very well have to refactor away from switch, or
at least mangle your switch badly, which would be pretty bad.
Is the following what you mean by "mangle your switch badly" ?
switch (o) {
case A: ...
case B: do some B-ish stuff ... also, if (g) {...}
case C: ...
...
case Z: ...
case Object: if (o instanceof B && !g) { do the B-ish non-g thing }
}
Is a guard (a) part of the `case` construct, or (b) part of the pattern
operand for a `case` construct? The original mail introduced "guard
expression" as "a boolean expression that conditions whether the case
matches", which sounds like (a). However, the purpose of a `case`
construct is to enumerate one or more possible values of the selector
expression, and if a `case` construct has a post-condition `& g()` then
it's not just enumerating, and it isn't a `case` construct anymore. I
mean, we don't want to see guards in the `case` constructs of legacy
switches, right? (`switch (i) { case 100 & g():`) So, is the answer (b) ?
Alex