Fwd: Observation about nulls in type patterns
Brian Goetz
brian.goetz at oracle.com
Mon Jul 27 20:25:19 UTC 2020
Received on the -comments list.
Indeed, we're well aware of the "action at a distance" concern you raise.
Zooming out, we have three broad choices for addressing the
null-hostility of switch as we generalize the switch feature to support
patterns:
- Abandon the existing switch construct for dead, and make a new one
with similar but different semantics (sometimes called "snitch");
- Accept the current limitations of switch, and propagate them
foreward, potentially creating new anomalies (such as the inability to
refactor if-else chains to switch);
- Partially rehabilitate the old feature, looking for ways to slot the
new functionality in between the obstacles of the old constraints.
All of these have pros and cons, and within each, there are
sub-approaches with pros and cons. We struggled with the same set of
issues with expression switch; there, the main legacy constraint there
was partiality (switch statements are partial, but expressions must be
total.) Again, we had the same basic three directions (with many
sub-directions), and concluded that rehabilitating switch was better
than either having both "switch" and "snitch", or allowing partial
switch expressions which would throw at runtime. The cost there is that
you have to look more closely at whether a switch is used as a statement
or expression to determine whether the cases cover all the possibilities.
With patterns, and with nullity, we again prefer the rehabilitation
path, and down that path, have explored several variants, such as
separate pattern denotations for non-nullable and nullable (e.g., T vs
T?), modifiers ("nullable-switch"), etc. The con of the path we have
taken, as you note, is that (especially if you combine multiple
features, like type inference and pattern switch), it is not as obvious
whether a pattern is total or not.
Of course, if you want to make it more obvious, you can write more
explicit code. You can, for example, refactor
switch (o.get1().get2()) {
case Integer i: ...
case Number n: ...
}
into
Number t = o.get1().get2();
switch (t) {
case Integer i: ...
case Number n: ...
}
and now it is more obvious again.
One thing that leads me to consider this as the least-bad alternative is
that switches throwing NPE today are not a serious problem, nor do
developers universally have to wrap a null check around switches to make
this so. Which suggests that this particular edge is not the sharpest,
so making it even less sharp (though more irregular) is unlikely to
cause serious pain.
And, it's not like there's a pain-free approach here; there's only
tradeoffs that move it around. Just like with everything else regarding
null. But, we hope the preview process will either validate, or
invalidate, this hypothesis -- I think we've gotten as far as we can go
with looking at it as a theoretical problem.
Cheers,
-Brian
-------- Forwarded Message --------
Subject: Observation about nulls in type patterns
Date: Sat, 25 Jul 2020 21:51:37 +0200
From: Jens Lideström <jens at lidestrom.se>
To: amber-spec-comments at openjdk.java.net
Brian Goetz posted a write-up of the feature Types patterns in switch in
amber-spec-experts on Jun 24:
https://mail.openjdk.java.net/pipermail/amber-spec-experts/2020-June/002235.html
I'd like to contribute an observation about the consequences of the
proposed mechanism for handling null values in switches:
If I understand the proposal correctly the behaviour of null values in a
switch will depend on the type of the expression being switch upon. This
is potentially non-local information, for example when the expression is a
field access or a method call.
To understand the behaviour of null values in a switch readers will have
to examine the source of the expression being switch upon. Also, if the
type of a method or a field is being narrowed or widened that change might
silently affect the behaviour of existing switches in other parts of the
code.
Example:
switch (o.get1().get2()) {
case Integer i: ...
case Number n: ...
}
If get2 is some method that is declared to return Number then the second
pattern is total and will accept null; if get2 is declared to return
Object pattern the IS total and WILL accept null.
The expert group is probably aware of this fact already but I think it
deserves to be noted explicitly.
Best regards,
Jens Lideström
Random Passer-by
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.java.net/pipermail/amber-spec-experts/attachments/20200727/e74604de/attachment.htm>
More information about the amber-spec-experts
mailing list