[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