It's a good time to check in on this list, because I think we've made a lot of progress in the last weak clearing away the layers of overgrowth that have gotten in the way of seeing a clear story.  Here's my summary of what's been cleared away.  (Reminder: this is the summary thread, so its for summary information only, if you want to argue with the specific points I'm making, take it to the right thread.)

Totality and patterns.  I think it's even more obvious at this point that the only realistic interpretation of `T t` as a pattern is that it is total on `T`, including null.  Anything else adds value-destroying complexity when we try to compose patterns.  But, it was hard to see this because ...

Nullity and switch.  We came at this with the mistaken assumption that "switches are just null-hostile."  But that was wrong.  After digging at it, we realized that the null-hostility of switch was an artifact of the limited domains to which switch had been originally applied.  When we zoom out, it becomes obvious that the null-hostility of switch is not scalable (we even tried to distort the semantics of patterns to try to accomodate it.)  The obvious move here is:

 - Allow `case null` in all reference switches, with the obvious semantics;
 - Enums, strings, and boxes get an implicit `case null: throw` at the top if there is no explicit `case null`;
 - Total patterns -- including default -- match null

So this means that _all_ switches are nullable, but these special target types bring their own special null defaults. While we first thought it might be the switch that was throwing, and then we thought maybe it was the (possibly implicit) default that was throwing, in reality, it is the invisible `case null` that comes with switching on these special types.

(Meta: this shows how much damage the "blow early, blow often" rule does -- by introducing ad-hoc rules to try to create a null-free playing field, trying to undo even one of these can take weeks of analysis to unravel.)

These two moves clear away almost all of the null hazards with switch, and put us on a principled foundation: with the exception of the legacy switch types, null is just another value that flows through patterns and switches, which can be matched, with the obvious and now-composible semantics.  I think this also should reduce the "totality is too subtle" concern, because we've put the null hostility into a more well-defined "box" -- switches are just nullable, so *of course* the nulls flow into the total pattern.


Separately, there are TWO issues regarding switch totality.  The first is how we can give statement switches the same error checking for totality that statement switches currently enjoy. I think there is npw room to cleanly repurpose `default` for this.

The second is, while tangentially related to nullity, is about _optimistic totality_.  The optimistic totality we embraced in expression switches over enums does not scale quite cleanly yet to sealed types, and specifically to _lifting_ type patterns over sealed types.  We have more work to do here.

(One of the underappreciated moves of the optimistic totality we did in 12 is that it is _better_ to leave out the default clause when a switch is believed to be optimistically total, because it leads to better type checking -- omissions and separate compilation artifacts are detected at compile time rather than runtime.  We would like to get the same for sealing.)


So, summary:

 - Type patterns `T t` are total on U <: T, and `var t` is total on all types;
 - Total patterns match null;
- switches are not null hostile;
- `default` is a total switch case;
 - you can say `case null` in switch;
 - For switches on *enums, strings, and boxes*, there is an implicit `case null` that throws, but you can override this with an explicit `case null`.  (There's still some fine points to discuss here; if we want `case null` to be able to fall into default, then we can't require it be at the top.)  - We can consider enhancing `default` to take a total destructuring pattern with minimal distortion;
 - We need to have a longer conversation about optimistic totality.

I think that's good progress for the week, as it checks off 3 of the items on this list.



On 8/14/2020 1:19 PM, Brian Goetz wrote:
Here's a summary of the issues raised in the reviews of the patterns-in-switch document.  I'm going to (try to) start a new thread for each of them; let's not reply to this one with new topics (or with discussion on these topics.)  I'll update this thread as we add or remove things from the list.

 - Is totality too subtle? (Remi) There is some concern that the notion of using totality to subsume nullability (at least in nested contexts) is sound, he is concerned that the difference between total and non-total patterns may be too subtle, and this may lead to NPE issues.  To evaluate this, we need to evaluate both the "is totality too subtle" and the "how much are we worried about NPE in this context" directions.

 - Guards.  (John, Tagir) There is acknowledgement that some sort of "whoops, not this case" support is needed in order to maintain switch as a useful construct in the face of richer case labels, but some disagreement about whether an imperative statement (e.g., continue) or a declarative guard (e.g., `when <predicate>`) is the right choice.

 - Exhaustiveness and null. (Tagir)  For sealed domains (enums and sealed types), we kind of cheated with expression switches because we could count on the switch filtering out the null. But Tagir raises an excellent point, which is that we do not yet have a sound definition of exhaustiveness that scales to nested patterns (do Box(Rect) and Box(Circle) cover Box(Shape)?)  This is an interaction between sealed types and patterns that needs to be ironed out.  (Thanks Tagir!)

 - Switch and null. (Tagir, Kevin)  Should we reconsider trying to rehabilitate switches null-acceptance?  There are several who are questioning whether this is trying to push things too far for too little benefit.

 - Rehabilitating default.  The current design leaves default to rot; it is possible it has a better role to play with respect to the rehabilitation of switch, such as signalling that the switch is total.

 - Restrictions on instanceof.  It has been proposed that we restrict total patterns from instanceof to avoid confusion; while no one has really objected, a few people have expressed mild discomfort.  Leaving it on the list for now until we resolve some of the other nullity questions.

 - Meta. (Brian)  Nearly all of this is about null.  Is it possible that everything else about the proposal is so perfect that there's nothing else to talk about?  Seems unlikely.  I recommend we turn up the attenuation knob on nullity issues to leave some oxygen for some of the other flowers.



Reply via email to