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.


Reply via email to