> On Jan 10, 2021, at 4:41 PM, Brian Goetz <brian.go...@oracle.com> wrote:
> 
> 
>>> 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.

Good.  I am, as always, confident that now that you understand the problem, you 
will choose a good solution.  :-)

(More near the bottom.)

>> 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.

Or we could just decide that “pattern and” will likely be used a lot in case 
labels, but hardly at all in `instanceof` expressions, and therefore just live 
with having to use parentheses in the latter case.  But even with this design 
choice, it’s handy to have patterns distinguishable from expressions, because 
it helps to catch bugs when you accidentally omit the parentheses.

> 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.

Okay, I’m going to ask a stupid question: does it have to be expressible as 
ASCII characters?

Oh, it does, huh?  :-)

So here’s an interesting design idea: (1) use both `&&&` and `&` (as well as 
`&&`), and allow only the following six meta-patterns of usage for conjunctive 
operators:

        pattern & pattern
        expression & expression
        expression && expression
        pattern &&& pattern
        pattern &&& expression
        expression &&& pattern

and the result is considered to be a pattern unless both operands are 
expressions.   Then make `instanceof` have lower precedence than a `&` or `&&&` 
to its right, but let it continue to have higher precedence than a `&&` to its 
right.

This is probably another of those things that would be much easier to use in 
practice (and to explain through examples) than to explain in theory.


Reply via email to