[pattern-switch] Guards

Brian Goetz brian.goetz at oracle.com
Sun Jan 10 21:41:33 UTC 2021


>> I agree such a move would be poor.  That was my motivation for 
>> suggesting & for "pattern conjunction" instead of &&; to relegate 
>> these concerns to irrelevance.  So
>>
>>     if (x instanceof P & X)
>>
>> says X is a pattern and we're testing x against the composite pattern 
>> P AND X, and
>>
>>     if (x instanceof P && X)
>>
>> says X is a boolean expression.
>
> And I am saying, yes we can do that and make it work, but only by 
> violating Java's ancient implicit design rule that operator precedence 
> obeys a total order.

Ah, now I get it.  I'm looking at what happens on the right side, and 
you're pointing out a problem before we even get there, which is: 
instanceof is going to gobble the first clause of the conjunction, 
whether the conjunction is joined with & or &&.  Got it.

> If I understand correctly, your proposal is that
>
>     if (x instanceof P & X)    means the same as     if (x instanceof 
> (P & X))     if X is a pattern
>
> but
>
>     if (x instanceof P & X)    means the same as     if ((x instanceof 
> P) & X)     if X is an expression
>
> (which has always been true, and I’m assuming you are not proposing a 
> big, incompatible change here).

Well, now that you put it that way, it doesn't sound as good.  But, 
let's pull on that string.

> That would mean that you have to commit to a grammar in which patterns 
> can in fact be distinguished from expressions.

This would be great, and not only for compilers -- it would be good if a 
human were not confused by "is that a pattern or an expression."  The 
last time we looked at this, we had identified three sources of 
ambiguity, two of which we've banished at least temporarily:

1.  A bare type as a pattern.  At first, we tried to retcon `T` in `x 
instanceof T` as being a pattern with no bindings.  But among other 
problems, that means that any bare identifier might be either a pattern 
or an expression.  We addressed this by adjusting the grammar around 
`instanceof`, to say there are two forms: `x instanceof Type` and `x 
instanceof Pattern`.

2.  Constants.  We initially thought constant patterns were a 
no-brainer, but then it is really hard to tell when `foo(0)` or 
`Bar(null)` is a pattern or an invocation.  We have tentatively 
addressed this by just not doing constant patterns, or, if we want to do 
them later, commit to coming up with a less ambiguous syntax (such as 
`== 0` for a constant pattern.)  But, the more I think about constant 
patterns, the weaker they seem as a feature; they don't obviate the need 
for guards, and the set of patterns that can use constants instead of 
guards is pretty small.  (C# went down the road of relational patterns 
such as `> 0`, but I'm not sure how much of a dent this makes either.)

3.  Nilary deconstruction patterns.  The remaining ambiguity (as far as 
I can tell) is `Foo()`; this could be an invocation or a match to a 
nilary deconstruction pattern.  (This stems in part from making the 
binding for the narrowed target optional; you can say `Foo(var x) f`, 
but the `f` is optional, because much of the time you don't need it.)  
Perhaps if we said that the narrowed target is required if there are no 
nested patterns, then we could banish this ambiguity.

So, we don't yet have the grammar where patterns are fully 
distinguishable, but we almost do.  So one path here is to finish that job.

For sake of exposition, I'll use &&& to mean "pattern and", since I want 
to separate the parsing details from the semantics.

> Note that if that latter fact is true, then you don’t need to say
>
> case P(var x) & true(x > 0):
>
> It should suffice to say
>
> case P(var x) & x > 0:

The essence of that approach is that we can treat any (boolean) 
expression as a pattern (which is not out of the question.)  Because, we 
may want to keep matching more stuff if the guard succeeds:

     case P(var x) &&& true(x > 0) &&& Q(var y):
          |                                   |
          -------------------------------------
                           |
                    one big pattern

So I think you're proposing:

  - Find a grammar where patterns and expressions are disjoint
  - Treat any (boolean) expression as a pattern when conjoined with &&& 
(or however we denote pattern conjunction.)

We flirted with this briefly in the past but got stuck on point (3) above.

Returning to the syntactic choice of conjunction, I'm also worried about 
the human concern of reusing &&, since we expect

     if (x instanceof P && somethingElse) { ... }

to be common, and it would be nice if users didn't have to work so hard 
to tell whether somethingElse was another pattern, or another clause.  
(Or worse, if there are many intermediate clauses, it surely won't be 
obvious if the last clause is a pattern.)  Using && inside of if 
statements is really common, so I don't want to invite this ambiguity, 
which is why I was thinking about &, which is less likely to elicit such 
confusion, but still may not work well enough.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.java.net/pipermail/amber-spec-experts/attachments/20210110/9656d1ac/attachment.htm>


More information about the amber-spec-experts mailing list