Re: Question about pattern matching and sealed types – redundant switch cases

Andreas Berheim Brudin andreas.brudin at gmail.com
Sat Apr 19 17:42:55 UTC 2025


True, you may very well have a point here.

I actually have the "andThen" method on my Result, but I call it "flatMap"
for consistency. Some people had a hard time understanding what was going
on and I don't want to be the one pushing for every developer needing a
degree in category theory so I am trying to introduce this safe programming
style without requiring familiarity with too advanced functional concepts
(even though many seem happy to flatMap their lists!).

But I guess just calling it "andThen" might alleviate some of the burden,
thanks!

/Andreas

On Sat, Apr 19, 2025 at 7:34 PM Brian Goetz <brian.goetz at oracle.com> wrote:

> Right, you've built a monad here, and you want to express the happy path
> with a monadic do.  In Haskell you'd write this as:
>
>     do
>         something <- getSomething()
>         result <- doThingWithSomething(something)
>
> and let the language desugar it accordingly.  But you can do this yourself
> in Java!
>
>     sealed interface Result<T> permits Ok, Err {
>         <U> Result<U> andThen(ThrowyFunction<T, U> f);
>         static<T> Result<T> of(ThrowySupplier<T> s) { ... }
>     }
>
> and then express your code as:
>
>     Result.of(() -> getSomething())
>           .andThen(C::doThingWithSomething)
>           ...
>
> No nested code needed.
>
>
> On 4/19/2025 1:26 PM, Andreas Berheim Brudin wrote:
>
> Thank you both for the quick replies.
>
> I guess the fact that b1 and b2 is available outside of the if, hints at
> some kind of flow-based type analysis (even though the new type is
> implicitly declared in the pattern), but I would then have proven Brian's
> point - that once that door is open, people will ask for more.
>
> But given the semantics of Java, I understand that this doesn't fit very
> well. The reason I ask was that we use a Result type (Ok/Err) quite a lot,
> and the biggest complaint is the nestedness of the code, which often
> becomes:
> return switch(getSomething()) {
>   case Err(var e) -> yield someError(e);
>   case Ok(var something) -> {
>     yield switch (doThingWithSomething(something)) {
>       case Err(var e) -> yield someOtherError(e);
>       case Ok(var result) -> etc...
>   }
> }
>
> I guess you don't have any quick fixes for that other than resorting to
> traditional exception handling.
>
> Best,
> Andreas
>
> On Sat, Apr 19, 2025 at 7:08 PM Remi Forax <forax at univ-mlv.fr> wrote:
>
>>
>>
>> ------------------------------
>>
>> *From: *"Andreas Berheim Brudin" <andreas.brudin at gmail.com>
>> *To: *"amber-dev" <amber-dev at openjdk.org>
>> *Sent: *Saturday, April 19, 2025 6:17:43 PM
>> *Subject: *Question about pattern matching and sealed types – redundant
>> switch cases
>>
>> Hi all,
>>
>> I'm new to the list—apologies if this has been discussed before, and
>> thanks in advance for your time.
>>
>> I have a question about pattern matching with sealed types. Consider this
>> example:
>>
>> sealed interface A permits B, C {}
>> record B(String b1, String b2) implements A {}
>> record C(String c) implements A {}
>>
>> A myVar = ...;
>> if (!(myVar instanceof B(var b1, var b2))) {
>>     return switch (myVar) {
>>         case C(var c) -> c;
>>         case B b -> throw new IllegalStateException("should not happen");
>>     };
>> }
>> // use b1, b2
>>
>> Here, I want to keep an early-return style, but since I need to return a
>> value from C, I have to use a switch. This leads to redundancy: we've
>> already tested myVar is not a B, so that case should be statically
>> unreachable.
>>
>> My questions:
>>
>> 1. Is there any consideration or ongoing work to allow the compiler to
>> automatically eliminate such unreachable cases?
>>
>>
>> The short answer is no :)
>>
>> Long answer,
>>   (1) myVar does not change its type, it is declared as an A, it always
>> be an A (Groovy or Kotlin behave differently, they use flow typing)
>>   (2) the type system of Java has no notion of exclusion, the type A but
>> not B does not exist
>>   (3) instanceof and switch does not have the same semantics, instanceof
>> has no notion of exhaustiveness while a switch on a sealed type has.
>>
>> so because of (1) the type of myVar can not be changed, because of (2)
>> its new type inside the if can not be "A but not B" and because of (3) the
>> new type can not be only C.
>>
>>
>>
>> 2. If only one case remains (in this case, C), could it be possible to
>> treat the variable as a C directly without requiring an explicit switch?
>>
>>
>> No,
>>
>>
>>
>> Again, apologies if this has already been discussed. I'd appreciate any
>> pointers to relevant threads or JEPs if so.
>>
>>
>> The relevant JEPs are JEP 394 (for instanceof) and 441 (for switch).
>>
>>
>>
>> Thanks,
>> Andreas
>>
>>
>> regards,
>> Rémi
>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-dev/attachments/20250419/6237e419/attachment.htm>


More information about the amber-dev mailing list