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