<div dir="ltr"><div class="gmail_default" style="font-family:monospace">Hello Brian,<br><br>Thank you for your response!<br><br>> Imagine it worked the way your intuition tells you it<br>> should work. I'll start with the old case, nested record<br>> patterns. Now, suppose you have:<br>> <br>> record Pair<T,U>(T t, U u) { }<br>> <br>> ...<br>> <br>> Pair<Pair<A, B>, Pair<C, D>> p = ...<br>> <br>> switch (p) {<br>> case Pair(Pair(A a, B b), Pair(C c, D d)): ...<br>> }<br>> <br>> Under the David rules, this switch is not exhaustive,<br>> because it matches neither Pair(null, _) or<br>> Pair(_, null). So what cases would you have to add to the<br>> switch to satisfy the compiler's complaints of<br>> non-exhaustiveness? Write the code, and then tell me if<br>> you want to program in that language...<br><br>Excellent example lol. That clarifies it perfectly. So, the reason why we do not want to have this level of specificity required by the programmer is because it would cascade into a giant pile of writing down edge cases. Makes perfect sense.<br><br>> The first part of the example (Box<Box<String>>) is not<br>> new to this JEP; this is how nested patterns work. (If<br>> you think about it for long enough, you realize that the<br>> alternatives are all nearly intolerable.) The<br>> Box<Integer> example just adds one more form of<br>> remainder.<br><br>I always try and think these questions through before asking them, but there's only so far my brain can stretch without trying things out myself. Thank you very much.<br><br>> The gap between exhaustiveness and totality (which we<br>> call "remainder") is indeed somewhat counterintuitive at<br>> first, because we would like for these two words to mean<br>> the same thing. There were many, many discussions<br>> regarding this in Record Patterns. This JEP extends the<br>> surprise a little bit, but only in a quantitative, not<br>> qualitative way; unboxing conversions are another place<br>> where we encounter a gap between these two similar<br>> concepts.<br><br>That word "Remainder" reminds me now. It looks like my exact question was already answered in the "Remainder" doc from a few months ago. <br><br><a href="https://openjdk.org/projects/amber/design-notes/patterns/exhaustiveness#nested-patterns-and-sealed-types">https://openjdk.org/projects/amber/design-notes/patterns/exhaustiveness#nested-patterns-and-sealed-types</a><br><br>In my defense though, that doc was pretty content-dense, full of explaining how the unintuitive is better than the alternative.<br><br>I guess another take away from this exchange is recognizing that what I have been looking at this entire time is called the "Remainder". Knowing that gives me a cue to start watching for nulls, as well as other possible remainders.<br><br>Thank you for your time and help!<br>David Alayachew<br></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Mon, Sep 25, 2023 at 8:07 PM Brian Goetz <<a href="mailto:brian.goetz@oracle.com">brian.goetz@oracle.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<div>
<font size="4"><font face="monospace">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.<br>
<br>
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.<br>
<br>
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:<br>
<br>
record Pair<T,U>(T t, U u) { }<br>
<br>
...<br>
<br>
Pair<Pair<A, B>, Pair<C, D>> p = ...<br>
<br>
switch (p) { <br>
case Pair(Pair(A a, B b), Pair(C c, D d)): ...<br>
}<br>
<br>
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...<br>
<br>
<br>
<br>
<br>
</font></font><br>
<div>On 9/25/2023 4:50 PM, David Alayachew
wrote:<br>
</div>
<blockquote type="cite">
<div dir="ltr">
<div class="gmail_default" style="font-family:monospace">Hello
Mark,<br>
<br>
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!<br>
<br>
I was very surprised to see the following snippet in the JEP.<br>
<br>
<br>
> With pattern labels involving record patterns, some<br>
> patterns are considered to be exhaustive even when they<br>
> are not unconditional. For example:<br>
> <br>
> Box<Box<String>> bbs = ...<br>
> switch (bbs) {<br>
> case Box(Box(String s)): ...<br>
> }<br>
> <br>
> This switch is considered exhaustive on
Box<Box<String>><br>
> even though the pattern Box(Box(String s)) will not match<br>
> the pathological value new Box(null), which is in the<br>
> remainder set and is handled by a synthetic default<br>
> clause that throws MatchException.<br>
> <br>
> With the introduction of primitive type patterns, we<br>
> observe that unboxing follows the same philosophy. For<br>
> example:<br>
> <br>
> Box<Integer> bi = ...<br>
> switch (bi) {<br>
> case Box(int i): ...<br>
> }<br>
> <br>
> This switch is considered exhaustive on
Box<Integer> even<br>
> though the pattern Box(int i) will not match the<br>
> pathological value new Box(null), which is in the<br>
> remainder set.<br>
<br>
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.<br>
<br>
The reason I feel that way is because I have a mental model in
my head that has the following rules.<br>
<br>
* If I have a top level pattern, it does not match null.
That's what case null is for.<br>
<br>
* For example, case String s, I do not expect s to be
null.<br>
<br>
* 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.<br>
<br>
* 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.<br>
<br>
* If I have a nested pattern, that pattern can match null.<br>
<br>
* For example, case Box(String s), I can expect s to maybe
be null.<br>
<br>
* 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.<br>
<br>
* 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.<br>
<br>
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?<br>
<br>
Thank you for your time and help!<br>
David Alayachew<br>
</div>
</div>
<br>
<div class="gmail_quote">
<div dir="ltr" class="gmail_attr">On Mon, Sep 25, 2023 at
10:09 AM Mark Reinhold <<a href="mailto:mark.reinhold@oracle.com" target="_blank">mark.reinhold@oracle.com</a>>
wrote:<br>
</div>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><a href="https://openjdk.org/jeps/455" rel="noreferrer" target="_blank">https://openjdk.org/jeps/455</a><br>
<br>
Summary: Enhance pattern matching by allowing primitive type
patterns<br>
to be used in all pattern contexts, align the semantics of
primitive<br>
type patterns with that of instanceof, and extend switch to
allow<br>
primitive constants as case labels. This is a preview
language feature.<br>
<br>
- Mark</blockquote>
</div>
</blockquote>
<br>
</div>
</blockquote></div>