[pattern-switch] Guards

Brian Goetz brian.goetz at oracle.com
Mon Jan 11 16:26:37 UTC 2021


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

The parentheses don't bother me so much.  I am more concerned about 
making it clear what is going on.  I see the following common case for 
AND-ed patterns in instanceof: when you are matching against a 
structured aggregate, like a Map or an XML blob:

     if (m instanceof (withMapping(k1, var v1) ANDP withMapping(k2, var 
v2))) { ... }

I can easily imagine cases where there are a ton of ANDed patterns for 
taking apart a document.  And there are likely to be guard/expressions 
mixed in too, since you might want to express constraints on previously 
extracted bits before extracting more bits.

I think the real question I'm struggling with here is: suppose we have 
AND patterns, which we will likely want sooner or later anyway (e.g. 
document deconstruction.)  So given that, does it make sense to have 
guards be a thing at all, or just find a way to turn boolean expressions 
into a sort of pattern?  And, if the latter, will users find it easier 
to just see boolean expressions as degenerate patterns, or to "wrap" a 
boolean expression in a pattern, as in `true(e)` or `when(e)`?

Finding a grammar for patterns that is disjoint to expressions (already 
hard) is only a necessary, but not sufficient, condition for making it 
sensible to treat boolean expressions as patterns.  I am worried about 
things like this:

     if (x instanceof P(var a, var b) & aSet.contains(a) & 
bSet.contains(b) & Q(var c, var d)) { ... }

Here, I match to P, do a bunch of potentially complex tests on the 
results, and then, if these tests succeed, keep matching the original 
target (x) to Q.  I worry that the user will have lost the thread of 
what is going on by then.  Whereas, if we rewrote as:

     if (x instanceof P(var a, var b) & true(aSet.contains(a)) & 
true(bSet.contains(b)) & Q(var c, var d)) { ... }

the true() is more of a signal for "I'm still in pattern-conjunction 
world", and I think it is less confusing what is going on by the time 
you get to the end.  Though this might just be the classic "new stuff 
wants to look new" bias.

One of the weaknesses of guards in switch is that once you have a guard, 
you've exited "pattern world", which is strictly less expressive than 
composing patterns with expressions into patterns. I am worried that 
even if the semantics work like the latter, it will still feel like the 
former to users.

As a thought-experiment, suppose that it was just impractical to have a 
separate grammar for expressions and patterns.  What would we do then?  
Would we then jump on something like `true(e)` or `when(e)` or 
`boolean(e)` as a pattern that takes a boolean expression argument and 
ignores its target?




More information about the amber-spec-experts mailing list