[pattern-switch] Guards

Guy Steele guy.steele at oracle.com
Mon Jan 11 05:58:52 UTC 2021



> On Jan 10, 2021, at 4:41 PM, Brian Goetz <brian.goetz at 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.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.java.net/pipermail/amber-spec-experts/attachments/20210111/9a1e69df/attachment-0001.htm>


More information about the amber-spec-experts mailing list