Guards

Maurizio Cimadamore maurizio.cimadamore at oracle.com
Fri Mar 5 22:11:31 UTC 2021


This seems like a nice landing.

The unification of guards and AND patterns was clever, and clearly 
compositional, but exposing AND patterns just to get to guards can seem 
a daunting step.

I agree that the priority, language design-wise is to get the combo 
switch/case and if/instanceof on roughly the same expressive footing. 
Without _some_ kind of guard-like capabilities, doing patterns in 
switches is gonna be very limited, and some code will necessarily fall 
off the expressiveness cliff and be rewritten as an if/else chain.

I think the `when(expr)` syntax is a minor detour; it's easy on the eye 
and, as you show, has a gentle progression as to where we are headed. 
Personally, given how common the basic guard use case is, I don't mind a 
little bit of sugar sprinkled here and there, even though yes, it does 
create two ways to do the same thing.

Cheers
Maurizio

On 05/03/2021 19:14, Brian Goetz wrote:
> Let me try and summarize all that has been said on the Guards topic.
>
> #### Background and requirements
>
> For `instanceof`, we don't need any sort of guard right now (with the 
> patterns we have); we can already conjoin arbitrary boolean 
> expressions with `&&` in all the contexts we can use `instanceof`, 
> because it's a boolean expression. (This may change in the future as 
> patterns get richer.)  So we can already express our canonical guarded 
> Point example with
>
>     if (p instanceof Point(var x, var y) && x > y) { ... }
>
> with code that no one will find confusing.
>
> For switch, we can't do this, because case labels are not boolean 
> expressions, they're some ad-hoc sub-language.  When the sub-language 
> was so limited that it could only express int and string constants, 
> this wasn't a problem; there was little refinement needed on `case 
> "Foo"`.
>
> As we make switch more powerful, we face a problem: if the user drifts 
> out of the territory of what can be expressed as case labels, they 
> fall off the cliff and have to refactor their 50-way switch into an 
> if-else chain.  This will be a really bad user experience.  Some sort 
> of escape hatch to boolean logic buys us insurance against this bad 
> experience -- as long as you can express your non-pattern criteria 
> with a boolean expression (which is pretty rich), you don't have to 
> leave switch-land.
>
> So we took as our requirement:
>
>     Some sort of guard construct that is usable in switch is a forced 
> move.
>
> #### Expressing guards in switch
>
> There are several ways to envision guards:
>
>  - As patterns that refine other patterns (e.g., a "true" pattern)
>  - As an additional feature of "case" in switch (e.g., a "when" clause)
>  - As an imperative control-flow statement usable in "switch" (e.g., 
> "continue")
>
> We've largely rejected the third (even though it is more primitive 
> than the others), because we think the resulting code will be much 
> harder to read and more error-prone.  We've bounced back and forth 
> between "let's nail something on the side of switch" and "let's let 
> the rising pattern tide lift all conditional constructs."
>
> Other languages have demonstrated that guards in switch-like 
> constructs are viable.
>
> The argument in favor of nailing something on the side of switch is 
> that it is pragmatic; it is immediately understandable, it raises the 
> expressivity of `switch` to where `if` already is, and it solves the 
> immediate requirement we have in adding patterns to switch.
>
> The argument against is that it is not a primitive; it is dominated by 
> the option of making patterns richer (by adding boolean patterns), it 
> is weak and non-compositional, and overly specific to switch.  (It is 
> possible to make worse-is-better arguments here that we should do this 
> anyway, but it's not really possible to seriously claim better, 
> despite attempts to the contrary.)
>
> #### Interpreting the feedback
>
> The JEP proposes a powerful and compositional approach:
>
>  - true/false patterns that accept arbitrary boolean expressions (and 
> which ignore their target);
>  - combining patterns with a pattern-AND combinator
>
> On the one hand, this is a principled, orthogonal, compositional, 
> expressive, broadly applicable approach, based on sensible primitives, 
> which will be usable in other contexts, and which anticipate future 
> requirements and directions.
>
> On the other hand, there has been a pretty powerful emotional 
> reaction, which could be summarized as "sorry, we're not ready for 
> this degree of generality yet with respect to patterns." This 
> emotional reaction seems to have two primary components:
>
>  - A "who moved my cheese" reaction to the overloading of `true` in 
> this way -- that `true` seems to be, in everyone's mind, a constant, 
> and seeing it as a pattern is at least temporarily jarring.  (This may 
> be a temporary reaction, but there's still a cost of burning through it.)
>
>  - A reaction to "borrowing & from the future" -- because the other 
> use cases for &-composition are not obvious or comfortable yet, the 
> use of &-composition seems foreign and forced, and accordingly 
> engenders a strong reaction.
>
> The former (which I think is felt more acutely) could be addressed by 
> taking a conditional keyword such as `when` here; ad-hoc "focus" 
> research suggests the negative reaction here is lower, but still there.
>
> The latter is, I think, the more linguistically significant of the 
> two; even though there is a strong motivation for & coming down the 
> pike, this is not the gentle introduction to pattern combination that 
> we'd like, and developer's mental models of patterns may not be 
> ready.  Patterns are still new, and we'd like for the initial 
> experience to make people want more, rather than scare them with too 
> much up front.
>
> #### Options
>
> I suspect that we'd get a lot of mileage out of just renaming true to 
> something like "when"; it avoids the "but that's not what true is" 
> reaction, and is readable enough:
>
>     case Foo(var x) & when(x > 0):
>
> but I think it will still be perceived as "glass half empty", with 
> lots of "why do I need the &" reactions.  And, in the trivial (but 
> likely quite common, at least initially) case of one pattern and one 
> guard, the answers are not likely to be very satisfying, no matter how 
> solidly grounded in reality, because the generality of the 
> compositional approach is not yet obvious enough to those seeing 
> patterns for the first time.
>
> I am not compelled by the direction of "just add guards to switch and 
> be done with it", because that's a job we're going to have to re-do 
> later.  But I think there's a small tweak which may help a lot: do 
> that job now, with only a small shadow of lasting damage:
>
>  - Expose `grobble(expr)` clauses as an option on pattern switch cases;
>
>  - When we introduce & combination (which can be deferred if we have a 
> switch guard now), plan for a `grobble(e)` pattern.  At that point,
>
>     case Foo(var x) grobble(x > 0):
>
> is revealed to be sugar for
>
>     case Foo(var x) & grobble(x > 0):
>
> As as bonus, we can use grobble by itself in pattern switches to 
> incorporate non-target criteria:
>
>     case grobble(e):
>
> which is later revealed to be sugar for:
>
>     case Foo(var _) & grobble(e):
>
> The downside here is that in the long run, we have something like the 
> C-style array declarations; in the trivial case of a single pattern 
> with a guard, you can leave in the & or leave it out, not unlike 
> declaring `int[] x` vs `int x[]`.  Like the "transitional" (but in 
> fact permanent) sop of C-style declarations, the "optional &" will 
> surely become an impediment ("why can I leave it out here, but not 
> there, that's inconsistent").
>
> All that said, this is probably an acceptable worse-is-better 
> direction, where in the short term users are not forced to confront a 
> model that they don't yet understand (or borrow concepts from the 
> future), with a path to sort-of-almost-unification in the future that 
> is probably acceptable.
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.java.net/pipermail/amber-spec-experts/attachments/20210305/69c94869/attachment.htm>


More information about the amber-spec-experts mailing list