[pattern-switch] Totality
Guy Steele
guy.steele at oracle.com
Mon Aug 24 03:35:14 UTC 2020
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 <guy.steele at oracle.com> 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.
>
More information about the amber-spec-experts
mailing list