Option 8 (“switch case (x) { … }”) is increasingly appealing to me, because it
is completely compatible, flags the variant up front rather than at the end, is
easily pronounced, and has a story that I think is simple to explain.
However, I would also like to offer this variant, which has two additional
constraints and is perhaps in some sense “the switch statement we wish we had
had all along”:
Option 9: The statement “switch case (x) { … }” is like “switch (x) { … }” but
insists that the value x be handled by some case clause. It is a static error
if any SwitchLabel of the switch statement begins with “default". It is a
static error if the set of case patterns is not at least optimistically total
on the type of x (therefore it is impossible for the switch statement to
silently do nothing), and you get residue checking. It is a static error if
the last BlockStatement in any SwitchBlockStatementGroup can complete normally.
It is a static error if any SwitchLabel of the switch statement is not part of
a SwitchBlockStatementGroup.
(The effect of the two additional constraints is to prevent fallthrough and
fallout. Thus under this definition a “switch case” always transfers control
to a nonempty set of BlockStatements that follows some switch label that begins
with “case”, and those statements cannot fall through—even the last set of
statements needs to have a “break” or something. Draconian, perhaps even
Procrustean, but opt-in.)
> On Aug 22, 2020, at 6:38 PM, Guy Steele <[email protected]> wrote:
>
> Option 8: The statement “switch case (x) { … }” is like “switch (x) { … }”
> but insists that the value x be handled by some case clause. The switch body
> cannot contain a default clause (static error if it does), and it’s
> impossible for the switch statement to silently do nothing. It’s a static
> error if the set of case patterns is not at least optimistically total, and
> you get residue checking.
>
> enum Color { RED, GREEN }
> Color x;
> switch (x) { case RED: … } // Okay
>
> enum Color { RED, GREEN }
> Color x;
> switch case (x) { case RED: … } // static error: cases are not
> optimistically total
>
> Note that you can still use int and String types, but because default clauses
> are forbidden, you have to use a total pattern instead:
>
> switch case (myString.length()) {
> case 2: case 3: case 5: case 7: primeSquawk();
> case 4: case 9: squareSquawk();
> case int n: squawk(n);
> }
>
> I think this option clearly dominates options 4 and 7 (“switch enum (x)” and
> “switch ((Color) x)”).
>
> Note that it’s not completely redundant to allow “switch case” expressions as
> well (which would ease refactoring), but the only extra constraint added by
> “switch case” is that a default clause cannot appear. If this option were
> adopted, I suspect it would quickly become idiomatic to use “switch case” on
> enums and many sealed types, and to use “switch” with a “default
> <totalpattern>” clause in most other cases.
>