[External] : Re: Two new draft pattern matching JEPs
Brian Goetz
brian.goetz at oracle.com
Thu Mar 4 16:28:05 UTC 2021
> I want to separate the discussions about & between patterns and true()/false() aka mix pattern and expressions,
> because the later is a call for trouble for me, the former is just a question about adding a new pattern or not.
>
> I see true() and false() has an heresy because conceptually a bunch of patterns is a kind of declarative API while expressions are not.
If it helps, let me trace the story of where the true/false pattern idea
came from. We have had "method patterns" (need a better name) in the
model forever; we have anticipated that some method patterns (e.g., map
get, regex match) will distinguish between input and output parameters.
And we have imagined AND and OR combinators for a long time as well.
One day, it occurred to me that, with the ultimate pattern model we are
envisioning, we don't need a linguistic notion of guards at all! I
could write an ordinary pattern:
static pattern guard(boolean b) {
if (!b) __FAIL;
}
and voila, we can express guards as:
case P & guard(e):
This was a powerful realization, because it meant that the notion of
"guard" was "not a primitive"; it was something we could compose from
existing envisioned tools. Except, we don't have method patterns yet
(and are not ready to do them), so ... why not treat the existing
reserved identifier `true` as if it were declared like `guard` above?
(What's weird about this pattern is that it ignores its target. That's
"clever", and clever can be confusing.)
I don't say this to justify the syntax; I get that pressing `true` into
service may feel a bit of a stretch. My point here is that the
true/false patterns proposed in the JEP are *not special*; the are
something we could declare eventually with the model that we expect to
get to. (They're like record patterns, in a sense; eventually all
classes will be able to declare deconstruction patterns, but records are
special in that they get one even without declaring it.) So if you find
them heretical, then you should also have a problem with expressing
map-get or regex match as a pattern, no? And if so, let's put the
complaint where it belongs; the use of expressions in true/false is just
a symptom of your distress, not the cause. Let's talk about the cause.
> Allowing expression right inside a pattern means that you can witness the evaluation order of patterns, something we don't want.
Note that this is unavoidable when we get to declared patterns; if there
are side effects, you can witness their order and arity. But, this is
also not your real point. So, let's get to the real point, so we can
discuss that, because whether we leak an unspecified order of evaluation
through side-effects is mostly a distraction.
> There is a misunderstanding here, i'm referring to the fact that
> case Point(1, 1):
> is actually rejected because it's too much like new Point(1,1) but at the same time, you want to allow expressions in the middle of patterns.
Actually, the opposite!
A lot of people (initially, me included) would like to just interpret a
boolean expression as a pattern:
case Foo(var x, var y) && x > y: ...
I found that goal compelling, but as patterns get more complicated, this
gets unreadable. A main benefit of the true() patterns (or, the
explicit guard() pattern declared above) is that it "quarantines" the
expression in an expression wrapper; there's a clear boundary between
"pattern world" and "expression world".
In any case, you are distorting the claim of "no expressions in the
middle of patterns." We have always envisioned specific contexts where
expressions can be dropped into patterns (map get, regex), but we have
worked to ensure there is a *clear syntactic division*, so that it is
clear from the syntactic structure whether something is a pattern or an
expression.
> For (a), yes it's switch specific and it's great because we don't need it for instanceof, you can already use && inside the if of an instanceof and you don't need it when declaring local variables because the pattern has to be total. So being specific to switch is not really an issue.
Only true if `instanceof` and `switch` are the only contexts where you
can imagine patterns. What about, say, `catch`? If you nail a bag on
the side of switch, you will have to nail the same bag on the side of
catch. Which might be acceptable, and the same bag might fit, but
that's yet more ad-hoc language surface.
> For (b), you can always shift all the contents of true() and false() to the right into a traditional guard, so we don't need true() and false()
In theory true, but only in a world where pattern evaluation is
cost-free, exception-free, and side-effect free. (That's why && is
short circuiting -- because these things are not always true.)
I'm not deaf to the argument that "nailing a bag on the side will be
easier for developers to accept", but please, let's stop pretending it's
not a bag. If you want to advocate for "bag", then please, make that
case directly!
More information about the amber-spec-experts
mailing list