[pattern-switch] Exhaustiveness
Brian Goetz
brian.goetz at oracle.com
Thu Aug 20 22:14:37 UTC 2020
I suspect there are other orderings too, such as "any nulls beat any
novels" or vice versa, which would also be deterministic and potentially
more natural to the user. But before we go there, I want to make sure
we have something where users can understand the exceptions that are
thrown without too much head-scratching.
If a user had:
case Box(Head)
case Box(Tail)
and a Box(null) arrived unexpectedly at the switch, would NPE really be
what they expect? An NPE happens when you _dereference_ a null. But no
one is deferencing anything here; it's just that Box(null) fell into
that middle space of "well, you didn't really cover it, but it's such a
silly case that I didn't want to make you cover it either, but here we
are and we have to do something." So maybe want some sort of
SillyCaseException (perhaps with a less silly name) for at least the
null residue.
On the other hand, ICCE for Box(novel) does seem reasonable because the
world really has changed in an incompatible way since the user wrote the
code, and they probably do want to be alerted to the fact that their
code is out of sync with the world.
Separately (but not really separately), I'd like to refine my claim that
`switch` is null-hostile. In reality, `switch` NPEs on null in three
cases: a null enum, String, or primitive box. And, in each of these
cases, it NPEs because (the implementation) really does dereference the
target! For a `String`, it calls `hashCode()`. For an `enum`, it calls
`ordinal()`. And for a box, it calls `xxxValue()`. It is _those_
methods that NPE, not the switch. (Yes, we could have designed it so
that the implementation did a null check before calling those things.)
> The ambiguity that this analysis still does not addresses situations
> such as D(E(novel, null)); this example is briefly alluded to at the
> end of Brian’s initial sketch of the formalism, but unfortunately the
> sketch does not address multi-parameter deconstructs in detail. So
> let’s go through this example: suppose that there are explicit cases
> that are optimistically total (I like the terminology Brian has
> provided) on D(E(Shape, Coin)), which might look like this:
>
> D(E(Round, Head))
> D(E(Round, Tail))
> D(E(Rect, Head))
> D(E(Rect, Tail))
>
> Then I think the residue would consist of
>
> D(null)
> D(novel)
> D(E(null, null))
> D(E(null, Head))
> D(E(null, Tail))
> D(E(null, novel))
> D(E(Round, null))
> D(E(Rect, null))
> D(E(Round, novel))
> D(E(Rect, novel))
> D(E(novel, null))
> D(E(novel, Head))
> D(E(novel, Tail))
> D(E(novel, novel))
>
> The order shown above is permissible, but some pairs may be traded,
> under the constraint that if two cases differ in one position and one
> of them has “null” in that position, then that one must come earlier.
>
> If we wish behavior to be deterministic, it would be Java-like to
> insist that (1) the cases be listed consistent with an increasing
> lexicographic partial order, where null < novel, and (2) that
> sub-patterns effectively be processed from left right. Under these
> rules, the cases
>
> D(E(null, null))
> D(E(null, novel))
>
> would raise NPE, and
>
> D(E(novel, null))
> D(E(novel, novel))
>
> would raise ICCE.
>
>
More information about the amber-spec-observers
mailing list