Sharing my experience with Switch Expressions
David Alayachew
davidalayachew at gmail.com
Sun May 11 14:39:52 UTC 2025
Hello Amber Dev Team,
I just wanted to share an experiment that I ran a long while back that made
refactoring sort of painful for me today.
I have a sealed interface called Cell.java, which has a deep inheritance
graph below it, all made up of datatypes that are very amenable for
edge-case checking via exhaustiveness checking. Aka, enums, sealed
interfaces, and records. And for records, I used LOTS of record patterns,
to extract and use the attributes I needed exhaustively.
The purpose was becase I was implementing a Path-Finding Algorithm, and I
needed to know what type of cells were in front of me in order to decide
the cost of going down a certain path (ex. walking on spikes uses up more
stamina then empty flooring). Basically, checking the current cell, and the
2 in front of me.
Originally, I implemented edge-case checking via a nested set of switch
expressions, all feeding into 1 final variable as the output. Using this, I
would check the data types of my current cell and the 2 in front of me, to
cover all possible combinations of Cell instances.
While this worked well enough, there were 2 problems with it.
1 - The verticality of it made it a little difficult to keep track of what
exactly I was checking at any given point in time. Sometimes, I would
accidentally skip over c2, for example, because I would be 5 levels deep in
a switch, checking the 3rd record attribute on c1, and then accidentally
skip c2 and just jump to c3.
2 - There were a lot of keywords getting in the way of the logic, making it
harder to read.
So, I thought to myself -- why don't I just flatten this hierarchy down to
a single switch expression? Patterns compose, after all.
What I did was make a local record Triple(Cell c1, Cell c2, Cell c3){},
then fed that into a single Switch Expression, then checked all that I
needed to using Record Patterns.
This definitely solved both of my problems. For 1, I used whitespace to
vertically align all of the record patterns for c1, c2, and c3. That made
it impossible for me to miss a Cell. And for 2, having a single case
contain nothing but record patterns meant that all of the fluff was out of
the way. Only the logic in front of me.
However, I exchanged 2 problems for 1.
A few days ago, I needed to do some refactoring to add a new feature to the
game. That meant adding 2 new data types (at different levels of my
hierarchy) to my Cell.
Well, when I did so, and pressed compile, I got the error message that I
was missing a case on my switch expression. Perfect, that's what I love
about Exhaustiveness Checking. The compiler becomes a "pit of success" of
sorts, where you press compile, and all the missing cases become
highlighted.
Except, that's not really what happened. All the error message told me was
that this switch was not exhaustive. Nothing about what I might be missing,
or an example of a possible, unaccounted for case.
I started searching and making changes where I thought be missing cases,
and found that it was actually super difficult to find all of the missing
spots. It took me almost 20-30 minutes just to see what I was missing.
Back when I had nested Switch Expressions, finding the error case was
trivial. Yes, it was the exact same error message, but since each Switch
Expression was so small and self-contained, no real digging had to occur to
figure out what case I was missing.
And that's when I realized my tradeoff -- flattening my switch expressions
meant that I had sacrificed traceability for readability. Finding my error
got much harder, now that I had made my code much more readable.
This was a bit frustrating to me, as I felt like I was forced to pick
between 2 unideal situations.
Anyways, readability is important to me, but traceability is even moreso,
so I will go back to nested Switch Expressions. I just wanted to announce
my experience, in case it helps.
Thank you for your time and attention!
David Alayachew
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-dev/attachments/20250511/70a98a94/attachment-0001.htm>
More information about the amber-dev
mailing list