[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