[pattern-switch] Totality

Brian Goetz brian.goetz at oracle.com
Sat Aug 22 18:06:13 UTC 2020


>> This might be stretching it a tad too far, but I like that we can 
>> given `default` useful new jobs to do in `switch` rather than just 
>> giving him a gold watch.
>
> This is a pretty good story, but I am sufficiently distressed over the 
> asymmetry of having to treat specially the last one of several 
> otherwise completely symmetric and equal cases:

Yeah, you should be distressed.  It is stretching it too far.  I think 
the first version of `default` -- total default with destructuring -- 
does work, and is a good new job for default.  But it doesn't quite 
stretch to address the issue that comes up from sealing.  So I'll put 
that in the "good try" bucket.

> First of all, let me note that after Brian’s detailed analysis about 
> the treatment of `null`, the only real difficulty we face is 
> compatibility with legacy switches on enum types.  We missed an 
> opportunity when enum was first introduced.  I really hate to 
> recommend an incompatible change to the language, but this message is 
> just brainstorming, so:
>
> Option 1: If the type of the switch expression is an enum or a sealed 
> type, then it is a static error if the patterns are not at least 
> optimistically total. **This would be an incompatible change with 
> respect to existing switches on enum types.**

Actually, as stated, this is not inconsistent -- for switch 
*expressions*.  We added switch expressions in Java 12 and required that 
they be total, and, when the target is an enum, we nodded to optimistic 
totality, by not requiring a `default` when the cases were 
optimistically total.  Where we don't have an equivalent story is for 
switch *statements*, which have always been partial -- and for which 
partiality is reasonable (just as an `if` without an `else` is 
reasonable.)  And the story for optimistic totality does not scale quite 
as well as we'd hoped to sealed types, for a few reasons:

  - Assuming `null` is always a mistake is an OK move for enums, but 
seems questionable for generalized sealed types;
  - If we want to lift optimistic totality through deconstruction 
patterns (Box(Head), Box(Tail) o.t. on Box), the shape of the residue 
gets complicated.

Remi a dites:

> If we go down to the route of saying that switch on enum, string and 
> box are special because null hostile,
> why not go a step further and say that, apart those switches and the 
> switch one primitive types, all other switches should be total, so 
> obviously an expression switch should be total but a statement switch 
> should be total too.

I see the attractiveness of this argument, but I don't think we can be 
that cavalier, for two main reasons:

  - There is a vaguely principled reason why these specific switches 
have special nullity behavior, which doesn't scale to general switches.  
So I think we can get away with "sealing" off the nullity behavior to 
the legacy cases, but only because the legacy cases actually have some 
nullity-relevance and the general case does not.

  - Partial statement switches are a totally reasonable thing, even on 
enums and sealed types!  They are the switch equivalent of an `if` 
without an `else`.  Which is an entirely reasonable thing to want to do, 
and preventing people from doing so is probably a cure worse than the 
disease.

In other words, having spent some time analyzing the history and 
assumptions, we see that the general nullity-behavior for a non-limited 
switch should be permissive (as argued on the other thread) and the 
current behavior a special case, and it is therefore reasonable to try 
to "seal" the null behavior off in a corner.   But it is not the case 
that the general totality behavior for switches should be "always 
total"; partial switches are fine, and there are lots of examples of such.

This argument reminds me of another switch oddity: fallthrough. 
Fallthrough is not a wrong feature; the wrong feature was fallthrough BY 
DEFAULT.  Similarly, partial switch is not a wrong feature; the wrong 
feature is "there's no way to engage totality checking."

> And now we only need to solve the problem of enums inside a statement 
> switch, here i disagree with Brian that it's a job for "default", as a 
> developer i want the compiler to emit an error at compile time not at 
> runtime.

You have misunderstood my proposal, then.  The errors would be at 
compile time, except for the residue (which has always been at runtime, 
it's just the residue is getting bigger.)

> I also want to add that if we add things like guards, we may also want 
> this kind of switches to be exhaustive,
>   int i = ...
>   switch(i) {
>     case i where i > 0: ...
>   }

Allow my to introduce my friend, Dr. Halting!  It is only the most 
trivial kinds of guards we can analyze for (optimistic) totality; it is 
my belief (though we should have that discussion (on another thread!)) 
that if we try to do any analysis of guard conditions here, it will be 
worse than if we try to do none.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.java.net/pipermail/amber-spec-experts/attachments/20200822/ff477cf3/attachment-0001.htm>


More information about the amber-spec-experts mailing list