Someone is confused by pattern matching (was: Pattern matching on primitive types mxied with instanceof is a footgun)
David Alayachew
davidalayachew at gmail.com
Thu Feb 6 08:30:31 UTC 2025
I feel like the counter argument to Tagir and Rémi is simple -- you missed
an edge case because you didn't use exhaustiveness checking. The entire
point of if is to opt into the one super specific case that you want. The
entire point of switch expressions is to make sure you covered all the
cases. Someone used a hammer when they needed a screwdriver.
If anything, this just demonstrates the frailety and weakness of if -- it
gives you so little in comparison to switch expressions. The fact that if
is more frequently used is because we didn't have this wildly better option.
Rémi, I really think the solution here is to look at if the same way we
looked at old-school switch statements -- as powerful tools that can easily
cut you if you don't hold them right. Also, that it is better to default to
the saner and easier tool when given the opportunity -- switch expressions.
And to close with a personal anecdote, in my own coding, I now treat if
statements theway I do old school for loops -- I use them only when I have
to, and eye them with greater scrutiny.
On Wed, Feb 5, 2025, 2:04 AM Brian Goetz <brian.goetz at oracle.com> wrote:
> One last comment on this topic: if we didn’t define primitive patterns
> this way, you would probably not like how constant patterns would work (or,
> alternately, would be inclined to define new asymmetric rules to help
> smooth out the gaps.)
>
>
> > On Feb 5, 2025, at 7:55 AM, Brian Goetz <brian.goetz at oracle.com> wrote:
> >
> > Let me address Tagir’s points:
> >
> >> On Feb 4, 2025, at 5:00 PM, Tagir Valeev <amaembo at gmail.com> wrote:
> >>
> >> Well, I would say that Remi has a point. This is a silent mistake that
> can be made.
> >
> > Agree, the construct can be used incorrectly; in this case the user
> thought something different from what was going to happen. It is not clear
> what they thought was going to happen, though; perhaps they mistakenly
> thought that the coordinates were ints, or perhaps they mistakenly thought
> that the floats would be lossily converted to ints, as would happen with an
> assignment or blind cast. If we don’t know what mistake they were making,
> it’s hard to correct for that (and even harder to correct for both.)
> >
> >> You don't see the declaration, as it's in another file so you may
> forget about original types and assume that they are ints.
> >
> > Yes, we discussed this a lot when we did record patterns. But I must
> point out here: this has absolutely zero to do with primitive patterns. It
> just so happened that in this example, primitives were used, but one could
> make the same mistake with a Box<Object> and a nested `String` pattern.
> >
> >> The code compiles and doesn't even throw an exception, it just works
> incorrectly.
> >
> > Here is where I disagree! It does work correctly! It asks whether the
> coordinates can be safely converted to integers, and only proceeds if that
> is the case. And that is a totally reasonable and useful question to ask!
> It is just that this is not what the user *thought* was going to happen
> (and, see above, we’re not even sure which mistake the user made). But
> what the user thought here is only one reasonable way to interpret what
> should happen.
> >
> > You could argue that this syntax is too “terse”, and this is the source
> of the problem; we could have specified nesting to require a claim of
> totality or partiality:
> >
> > case Box(partial String s):
> > case Box(total Object o):
> >
> > But I don’t think anyone would have thanked us for that explicitness.
> (And if the markers were optional, the mistake could still happen.)
> >
> >> It would be justified if such code pattern (converting from double to
> int only when double fits the int) was common, so conditional narrowing
> could be useful in many other places. But the fact is that it's highly
> uncommon. Nobody does this. Many other pattern matching features are nice.
> E.g., I often see the code which can be migrated to pattern switch (we are
> still on Java 17) and I really want to use it. However, I never see the
> code which can be migrated to a narrowing primitive pattern.
> >>
> >> I don't agree that we should drop primitive patterns completely, but we
> may reconsider floating point <-> integral conversions and disable them in
> patterns for a while (enabling them back later will be always possible).
> >
> >
> > Both of these arguments are dangerous arguments; they argue to introduce
> arbitrary asymmetries in how it works, just to match our guesses about
> “what users think.” In the first case, you’re saying that we should
> forever treat primitives differently from objects (because they work
> differently today and we have gotten used to it?); in the second, that
> instanceof should diverge from casting. Being able to interpret a pattern
> match by asking “what would casting do” is a very powerful technique.
> Undermining that to avoid a little bit of discomfort would be a huge
> mistake.
> >
> > I get how these feel like we are “helping” users by protecting them from
> foreseeable mistakes. But they are also not helping, by making the
> language more complicated, arbitrary, and asymmetric (and less
> expressive). Both paths offer the chance for confusion, but if we choose
> arbitrary complexity, the confusion is endemic and permanent, whereas if we
> choose simplicity and uniformity, the confusion eventually recedes into the
> past.
> >
> >
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-spec-observers/attachments/20250206/bfdee530/attachment-0001.htm>
More information about the amber-spec-observers
mailing list