[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