Positioning of guards (was: Reviewing feedback on patterns in switch)

Tagir Valeev amaembo at gmail.com
Wed Jan 26 03:50:05 UTC 2022


For the record: I like the current version with &&. It's short and
easy to understand (as people already know what && means in Java). I
see no reason in replacing it with `when`, which is more limiting.

> because of the potential confusion should we ever choose to support switch over boolean

It looks like any boolean expression that is a potentially constant
differs syntactically from the guarded pattern, so we can distinguish
between guarded pattern with && and boolean expression on AST level
without resolving the references. End users will unlikely use anything
other than explicit 'true' and 'false' constants, so it will add some
complexity to the compiler but does not add any problems to real users

> because the && did not stand out enough as turning a total pattern into a partial one

I think it's a matter of taste and habit. I, for one, already get used
to it. It signals about partiality much more, compared to a simple
type pattern. Looking at `CharSequence cs`, you cannot say whether
it's total or not if you don't know the type of the selector
expression. However, looking at `CharSequence cs && cs.length() > 0`
you are completely sure it's not total. So if we need a clear signal
to tell total and partial patterns apart, we have much bigger problems
with type patterns.

> Guarded patterns are never total
Except when guard is a constant expression that evaluates to `true`:

void test(Object obj) {
    switch (obj) { // compiles
        case Object s && true -> System.out.println(s);

On Wed, Jan 26, 2022 at 2:49 AM Brian Goetz <brian.goetz at oracle.com> wrote:
> > 2.  Positioning of guards
> We received several forms of feedback over the form and placement of guarded patterns.  Recall that we define a guarded pattern to be `P && g`, where P is a pattern and g is a boolean expression.  Guarded patterns are never total.  Note that we had a choice of the guard being part of the pattern, or being part of the `case` label; the current status chooses the former.  (Part of our reasoning was that there might be other partial pattern contexts coming, and we didn’t want to solve this problem each time. (For intsanceof, it makes no difference.) )
> I am prepared to reconsider the association of the guard with the pattern, and instead treat it as part of the case.  This is expressively weaker but may have other advantages.
> Additionally, people objected to the use of &&, not necessarily because “keywords are better”, but because of the potential confusion should we ever choose to support switch over boolean, and because the && did not stand out enough as turning a total pattern into a partial one.  What the alternative looks like is something like:
>     switch (x) {
>         case Foo(var x, var y)
>             when x == y -> A;
>         case Foo(var x, var y) -> B;
>     }
> Here, `when` (bike shed to be painted separately) is a qualifier on the case, not the pattern.  A total pattern with a `when` is considered a partial case.  This simplifies patterns, and moves the complexity of guards into switch, where arguably it belongs.
> The loss of expressiveness is in not allowing nested patterns like:
>     P(Q && guard)
> and instead having to move the guard to after the matching construct.  Some users recoiled at seeing guards inside pattern invocations; it seemed to some like mixing two things that should stay separate.  (For unrolling a nested pattern, `case P(Q)` where Q is not total unrolls to `case Pvar alpha) when alpha instanceof Q`.)

More information about the amber-spec-experts mailing list