Java 21 guarded pattern exhaustiveness
Asked Answered
C

1

10

I started to play with the new Java 21 feature - pattern matching.

public class Main {

  public static void main(String[] args) {

    RecordB recordB = new RecordB(true);

    switch(recordB) {
      case RecordB b when b.bool() -> System.out.println("It's true");
      case RecordB b when !b.bool() -> System.out.println("It's false");
    }

  }

  record RecordB(boolean bool) { }
}

When compiling the code above, the compiler yields information that the switch statement does not cover all possible input values

This is not necessarily true from my perspective. So here is my question: does any guarded pattern always make the switch expression non-exhaustive for the compiler or I am missing something here?

Curculio answered 27/9, 2023 at 16:46 Comment(8)
The example in the JEP shows the null case being handled, as lealceldeiro mentions in his answer.Evidently
@user85421, according to the specs, it should be case null, default -> {}, but, for the matter, even default -> {} alone "solves" it.Umbrella
@user85421, I know... I was just replying to your previous comment... the OP is asking about exhaustiveness related to guarded, unguarded pattern... the default case just happens to be mentioned in the specs I linked.Umbrella
hey @user85421, I'm sorry if I created some confusion. With my last comment I was trying to clarify what was meant by case null, default. I posted it to reply to (part) of your comment "... not sure if that is what lealceldeiro mentions in his answer...". As you correctly clarified case null -> { } does not solve the problem. And then I mentioned that this case null, default is what is officially stated in the JLS. Again, I agree with you: just adding a default to this code defeats the purpose of the question.Umbrella
in short, and as I see it for this case, there must be an unguarded case.Umbrella
Letting the exhaustiveness issue aside, I’d rather use case RecordB(var b) when b -> … and case RecordB(var b) when !b -> …Brassy
No, it is not the case that "any guarded pattern makes the switch non-exhaustive", but guarded patterns are not considered for purposes of computing exhaustiveness (which is probably what you meant.)Abad
I think what you'd like to ask is whether it would be possible for the compiler to analyze the when predicates and figure out that things like x>0 and x<=0 together are exhaustive. While this would be nice, this is impossible in the general case (see, e.g. Rice's theorem), and the boundary between what an arbitrarily smart algorithm can figure out or not would be complicated and surprising to users. So we instead opted to draw a simple, bright line, rather than navigating a fractal boundary (likely iteratively).Abad
U
12

does any guarded pattern always make the switch expression non-exhaustive for the compiler or I am missing something here?

No, it's not that "any guarded pattern makes the switch statement non-exhaustive", but there must be at least one unguarded pattern covering the type. If there is not any such unguarded pattern, it makes it non-exhaustive.

More formally (see JLS 14.11.1.1. Exhaustive Switch Blocks),

The switch block of a switch expression or switch statement is exhaustive for a selector expression e if one of the following cases applies:

  • There is a default label associated with the switch block.

  • There is a case null, default label associated with the switch block.

  • The set containing all the case constants and case patterns appearing in an unguarded case label (collectively known as case elements) associated with the switch block is non-empty and covers the type of the selector expression e.

The key point here is non-empty.

In your case the set containing the case patterns appearing in an unguarded case label is empty.


Side note:

After JEP 455 Primitive types in Patterns, instanceof, and switch is implemented, the switch statement could be exhaustive if a nested pattern is used to access bool value directly (see comments below from davidalayachew).

Umbrella answered 27/9, 2023 at 17:48 Comment(5)
This is great. But it would be good to edit your answer to add that this will very quickly become out of date, as they just announced the JEP for Primitive Patterns. That means that you can forgo the guard entirely and just use nested patterns, gaining both exhaustiveness and conciseness. Here is the link to the Primitive Patterns JEP -- mail.openjdk.org/pipermail/amber-dev/2023-September/008277.htmlVoluntary
hey @davidalayachew, thanks for the info! I'm not sure if I should edit it because the original question is not using nested patterns... probably a side note is ok.Umbrella
The only reason I suggest editing it is because the question is sort of an XY Problem -- en.wikipedia.org/wiki/XY_problem -- Long story short, OP wants to know how to get exhaustiveness while using a guard clause, because how else do they cover the true/false case? Well, now that Primitive Patterns are on their way, that is an alternative that meets their needs in an even better way than intended. It's all good if you feel different. Like you said, a side note is good too.Voluntary
@davidalayachew, thanks for giving your point of view here -- I added a side note. Feel free to suggest any edits to the answer if you think something can be improved there :)Umbrella
Nope, looks perfect. Ty vm.Voluntary

© 2022 - 2024 — McMap. All rights reserved.