[pattern-switch] Is totality too subtle?
Brian Goetz
brian.goetz at oracle.com
Fri Aug 14 17:20:33 UTC 2020
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.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.java.net/pipermail/amber-spec-experts/attachments/20200814/3685b033/attachment-0001.htm>
More information about the amber-spec-experts
mailing list