I just want to summarize what's been said on this:
- 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.
... but I'd like to do so without repeating what has been said before. So please, new observations only.
A key assumption underlying the proposed semantics of nullity in patterns (not switches) stems from the notion that definition of matching a pattern should be independent of the semantics of its consuming construct. Instanceof and switch may have pre-existing opinions about patterns, but we should be wary about polluting those. A pattern should mean something on its own.
Totality is defined relative to a target type; `String s` may be total on String, but definitely not on Object. We can view a nested pattern as a tree; as Guy observed, totality is a property of a _sub-tree_ of this tree, which might be one leaf, the whole tree, or some sub-tree of the tree. But a node cannot be total on its target if the sub-trees are not total on their corresponding target.
Further, the above property is desirable; we anticipate that catch-alls for an entire pattern chain, or for a sub-chain of a pattern chain, will be common, because they are useful; being able to say "Box containing anything", and destructure it at the same time, is an essential case that pattern matching should cover. This requires the existence of leaf (type) patterns that are nullable. One way to get there is with the current proposal; another is to have two of every kind of pattern (or a modifier on patterns) to add in or subtract out nullity.
The main concern, if I understand it correctly, comes in the context of switch. The switch header is already "at a distance" from the possibly-total (at the bottom); the type of the switch target may be further "at a distance" (because of var, expression nesting, etc), and so the concern is it may not be sufficiently obvious that a pattern is total, and therefore the semantics of the switch may not be sufficiently obvious.
To the extent there are two ways to write a pattern (or compose two patterns), identical except for nullability, the choice of which is the default (nullable or not) is extremely consequential for actual user experience.
I think the above is largely neutral and agreed on. Now, some personal observations:
- I think the concern that "it will be too hard to tell if a switch is meant to be exhaustive" is overblown. Catch-all switches typically look like catch-alls, both because of what they say and where they are placed. There will be common patterns of cases, which may not be familiar to everyone now, but will be very familiar soon enough, which will provide significant context for helping to understand the author intent.
- Even if the above concern is not overblown, I think the consequences of getting it wrong may still be. Yes, the null-handling behaviors of some switches may not be obvious, but: (a) right now, no switch ever deals with null at all, and there is not an epidemic of NPEs flying out of switches, and (b) what this does is move the null handling from a place where it always throws to a place where it might throw if the user is not careful. Multiplying the low probability of nulls showing up at the gate unexpected in the first place, by the low conditional probability that this will make things worse, it seems like we're pretty deep in corner-case-of-a-corner-case.
- I think that many of the proposed "remedies" are misguided. Many of the action-at-a-distance concerns can be likened to the interaction between `var` and diamond; yes, when you combine two features that have some degree of implicitness, things get less obvious. But the answer is not "don't do var", or "disallow var in the presence of diamond" (because they do interact in a well-behaved and potentially useful way), it is to warn users to not go overboard on being implicit everywhere they possibly can; use `var` where it adds value, and don't use it where it doesn't.
So, while I am sympathetic to the concerns, I am skeptical that this is such a big problem that we have to distort the language (and force users to reason about nullity on every pattern.) All the cures proposed so far seem much worse than the disease.
We still need to work through the other nullity issues, which might cause some rearrangement of the deck chairs. So at the very least, let's let this one lie for a while, until the others are worked out, but assuming that causes no change, I am of the opinion that we should do nothing now, and revisit in Preview/2 in light of actual experience, to see if it turns out that people can't handle the current behavior. I think we're in deep danger of extrapolating from an abstract fear.