New candidate JEP: 455: Primitive types in Patterns, instanceof, and switch (Preview)

Brian Goetz brian.goetz at oracle.com
Tue Sep 26 00:07:08 UTC 2023


The gap between exhaustiveness and totality (which we call "remainder") 
is indeed somewhat counterintuitive at first, because we would like for 
these two words to mean the same thing.  There were many, many 
discussions regarding this in Record Patterns.  This JEP extends the 
surprise a little bit, but only in a quantitative, not qualitative way; 
unboxing conversions are another place where we encounter a gap between 
these two similar concepts.

The first part of the example (Box<Box<String>>) is not new to this JEP; 
this is how nested patterns work.  (If you think about it for long 
enough, you realize that the alternatives are all nearly intolerable.)  
The Box<Integer> example just adds one more form of remainder.

Imagine it worked the way your intuition tells you it should work.  I'll 
start with the old case, nested record patterns. Now, suppose you have:

     record Pair<T,U>(T t, U u) { }

...

     Pair<Pair<A, B>, Pair<C, D>> p = ...

     switch (p) {
         case Pair(Pair(A a, B b), Pair(C c, D d)): ...
     }

Under the David rules, this switch is not exhaustive, because it matches 
neither Pair(null, _) or Pair(_, null).  So what cases would you have to 
add to the switch to satisfy the compiler's complaints of 
non-exhaustiveness?  Write the code, and then tell me if you want to 
program in that language...





On 9/25/2023 4:50 PM, David Alayachew wrote:
> Hello Mark,
>
> Thank you for posting this! I am shocked to see the Level of Effort 
> set to M -- all those sharp edges makes this seem much more complex!
>
> I was very surprised to see the following snippet in the JEP.
>
>
> > With pattern labels involving record patterns, some
> > patterns are considered to be exhaustive even when they
> > are not unconditional. For example:
> >
> > Box<Box<String>> bbs = ...
> > switch (bbs) {
> >     case Box(Box(String s)): ...
> > }
> >
> > This switch is considered exhaustive on Box<Box<String>>
> > even though the pattern Box(Box(String s)) will not match
> > the pathological value new Box(null), which is in the
> > remainder set and is handled by a synthetic default
> > clause that throws MatchException.
> >
> > With the introduction of primitive type patterns, we
> > observe that unboxing follows the same philosophy. For
> > example:
> >
> > Box<Integer> bi = ...
> > switch (bi) {
> >     case Box(int i): ...
> > }
> >
> > This switch is considered exhaustive on Box<Integer> even
> > though the pattern Box(int i) will not match the
> > pathological value new Box(null), which is in the
> > remainder set.
>
> This surprises me because it feels like the analogy of 
> Box<Box<String>> is a poor comparison to this. More specifically, it 
> feels like comparing Apples and Oranges.
>
> The reason I feel that way is because I have a mental model in my head 
> that has the following rules.
>
> * If I have a top level pattern, it does not match null. That's what 
> case null is for.
>
>     * For example, case String s, I do not expect s to be null.
>
>    * Because of this, I understand why a case int i would be 
> considered exhaustive, it seems to follow the same rules as a type 
> pattern.
>
> * And more specifically, the rules of exhaustiveness seem to align 
> here too. If instead of case String s, I had case Foo f, I would 
> assume that the pattern is exhaustive if (f) can match the full 
> domain, not including null.
>
> * If I have a nested pattern, that pattern can match null.
>
>     * For example, case Box(String s), I can expect s to maybe be null.
>
> * Because of this, I do not understand why a case int i would be 
> considered exhaustive, because it seems to break from the rules of a 
> type pattern thus far.
>
> * To give an example, if I have record Bar(int i), and then I later 
> refactor that to be record Bar(Integer i), I would hope that my type 
> pattern would no longer be exhaustive. But it sounds a lot like the 
> above passage implies it WOULD be exhaustive.
>
> I do not understand this decision. Could you help me understand the 
> what and the why? I also want to know your response to the sharp 
> corner I raised when it comes to refactoring primitives to and from 
> their boxed variants. Since Valhalla is on its way (hopefully bringing 
> with it the ability to opt-in and opt-out of nullability), it feels 
> like this sharp corner is going to protrude even further and be even 
> sharper. Could you address that concern too please?
>
> Thank you for your time and help!
> David Alayachew
>
> On Mon, Sep 25, 2023 at 10:09 AM Mark Reinhold 
> <mark.reinhold at oracle.com> wrote:
>
>     https://openjdk.org/jeps/455
>
>       Summary: Enhance pattern matching by allowing primitive type
>     patterns
>       to be used in all pattern contexts, align the semantics of primitive
>       type patterns with that of instanceof, and extend switch to allow
>       primitive constants as case labels. This is a preview language
>     feature.
>
>     - Mark
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/jdk-dev/attachments/20230925/7fb4d266/attachment.htm>


More information about the jdk-dev mailing list