Re: Reviewing feedback on patterns in switch

2022-02-15 Thread Brian Goetz
We're preparing a third preview of type patterns in switch.  Normally we 
would release after a second preview, but (a) we're about to get record 
patterns, which may disclose additional issues with switch, so best to 
keep it open for at least another round, and (b) we're proposing some 
nontrivial changes which deserve another preview.


Here's where we are on these.


1.  Treatment of total patterns in switch / instanceof


Quite honestly, in hindsight, I don't know why we didn't see this 
sooner; the incremental evolution proposed here is more principled than 
where we were in the previous round; now the construct (instanceof, 
switch, etc) *always* gets first crack at enforcing its nullity (and 
exception) opinions, and *then* delegates to the matching semantics of 
the pattern if it decides to do so.  This fully separates pattern 
semantics from conditional construct semantics, rather than complecting 
them (which in turn deprived users of seeing the model more clearly.)  
In hindsight, this is a no-brainer (which is why we preview things.)  
We'll be addressing this in the 3rd preview.



2.  Positioning of guards


Making guards part of switch also feels like a better factoring than 
making them part of patterns; it simplifies patterns and totality, and 
puts switch on a more equal footing with our other conditional 
constructs.  We did go back and forth a few times on this, but having 
given this a few weeks to settle, I'm pretty convinced we'd regret going 
the other way.


There were two sub-points here: (a) is the guard part of the pattern or 
part of switch, and (b) the syntax.  There was general agreement on (a), 
but some had preference for && on (b).  I spent some more time thinking 
about this choice, and have come down firmly on the `when` side of the 
house as a result for a number of reasons.


 - Possibility for ambiguity.  If switching over booleans (which we 
will surely eventually be forced into), locutions like `case false && 
false` will be very confusing; it's pure puzzler territory.
 - && has a stronger precedence than keyword-based operators like 
`instanceof`'; we want guards to be weakest here.
 - Using && will confuse users about whether it is part of the 
expression, or part of the switch statement.  If we're deciding it is 
part of the switch, this should be clear, and a `when` clause makes that 
clear.
 - There are future constructs that may take patterns, and may (or may 
not) want to express guard-like behavior, such as `let` statements 
(e.g., let .. when .. else.)  Expressing guards here with && is even 
less evocative of "guard condition" than it is with switches.

 - Users coming from other languages will find `case...when` quite clear.
 - We've talked about "targetless" switches as a possible future 
feature, which express multi-way conditionals:


    switch {
    case when (today() == TUESDAY): ...
    case when (location() == GREENLAND): ...
    ...
    }

This would look quite silly with &&.  Similarly, one could mix guards 
with a targeted switch:


    switch (x) {
    case Time t: ...
    case Place p: ...
    default when (today() == TUESDAY): ... tuesday-specific default
    default: ... regular default ...

Expressing guards that are the whole condition with `when` is much more 
natural than with &&.


tl;dr: inventing a `when` modifier on switch now will save us from 
having to invent something else in the future; choosing && will not.


We can continue to discuss the bikeshed at low volume (at least until we 
start repeating ourselves), but we need to address both of these points 
in the 3rd preview.



3.  Type refinements for GADTs


I've been working through the details here, and there are a number of 
additional touch points where GADTs can provide type refinement, not 
just on the RHS of a case, such as totality and inference.  I'll be 
pulling all these together to try to get a total picture here. It's not 
a blocker for the 3rd preview, it can be a future refinement.



4.  Diamond for type patterns (and record patterns)
This seems desirable, but there are details to work out.  It's not a 
blocker for the 3rd preview, it can be a future refinement.

Re: Reviewing feedback on patterns in switch

2022-02-15 Thread Remi Forax
> From: "Brian Goetz" 
> To: "amber-spec-experts" 
> Sent: Tuesday, February 15, 2022 7:50:06 PM
> Subject: Re: Reviewing feedback on patterns in switch

> We're preparing a third preview of type patterns in switch. Normally we would
> release after a second preview, but (a) we're about to get record patterns,
> which may disclose additional issues with switch, so best to keep it open for
> at least another round, and (b) we're proposing some nontrivial changes which
> deserve another preview.

> Here's where we are on these.

>> 1.  Treatment of total patterns in switch / instanceof

> Quite honestly, in hindsight, I don't know why we didn't see this sooner; the
> incremental evolution proposed here is more principled than where we were in
> the previous round; now the construct (instanceof, switch, etc) *always* gets
> first crack at enforcing its nullity (and exception) opinions, and *then*
> delegates to the matching semantics of the pattern if it decides to do so. 
> This
> fully separates pattern semantics from conditional construct semantics, rather
> than complecting them (which in turn deprived users of seeing the model more
> clearly.) In hindsight, this is a no-brainer (which is why we preview things.)
> We'll be addressing this in the 3rd preview.
Not sure it's a no-brainer. 
The question is more a question of consistency. There are two consistencies and 
we have to choose one, either switch never allows null by default and users 
have to opt-in with case null or we want patterns to behave the same way if 
they are declared at top-level or if they are nested. I would say that the 
semantics you propose is more like the current Java and the other semantics is 
more like the Java of a future (if we choose the second option). 

I think we should try the semantics you propose and see if people agree or not. 

>> 2.  Positioning of guards

> Making guards part of switch also feels like a better factoring than making 
> them
> part of patterns; it simplifies patterns and totality, and puts switch on a
> more equal footing with our other conditional constructs. We did go back and
> forth a few times on this, but having given this a few weeks to settle, I'm
> pretty convinced we'd regret going the other way.

> There were two sub-points here: (a) is the guard part of the pattern or part 
> of
> switch, and (b) the syntax. There was general agreement on (a), but some had
> preference for && on (b). I spent some more time thinking about this choice,
> and have come down firmly on the `when` side of the house as a result for a
> number of reasons.
Still agree on (a) 

> - Possibility for ambiguity. If switching over booleans (which we will surely
> eventually be forced into), locutions like `case false && false` will be very
> confusing; it's pure puzzler territory.
> - && has a stronger precedence than keyword-based operators like 
> `instanceof`';
> we want guards to be weakest here.
I don't understand your point, we want instanceof pattern && expression to be 
equivalent to instanceof type && expression + cast, so the fact that && has a 
stronger precedence makes that possible so it's not an issue. 

> - Using && will confuse users about whether it is part of the expression, or
> part of the switch statement. If we're deciding it is part of the switch, this
> should be clear, and a `when` clause makes that clear.
I don't think it's that important, apart if we start to also want to combine 
patterns with && 

> - There are future constructs that may take patterns, and may (or may not) 
> want
> to express guard-like behavior, such as `let` statements (e.g., let .. when ..
> else.) Expressing guards here with && is even less evocative of "guard
> condition" than it is with switches.
It's not clear to me how to use "let when else". Is it more like a ?: in C than 
the let in in Caml ? 

> - Users coming from other languages will find `case...when` quite clear.
> - We've talked about "targetless" switches as a possible future feature, which
> express multi-way conditionals:

> switch {
> case when (today() == TUESDAY): ...
> case when (location() == GREENLAND): ...
> ...
> }

> This would look quite silly with &&.
For me, this is like cond in Lisp but more verbose. using "case" and "when" 
here is sillly. 

> Similarly, one could mix guards with a targeted switch:

> switch (x) {
> case Time t: ...
> case Place p: ...
> default when (today() == TUESDAY): ... tuesday-specific default
> default: ... regular default ...
default && today() == TUESDAY is fine for me. 

> Expressing guards that are the whole condition with `when` is much more 
> natural
> than with &&.
For me, && is more natural than "when" because i've written more switch that 
uses && than "when". 
And don't forget that unlike most of the code, with pattern matching the number 
of characters does matter, this is more similar to lambdas, if what you write 
is too verbose, you will not write it. 

> tl;dr: inventing a `when` modifier on switch no

Re: Reviewing feedback on patterns in switch

2022-02-15 Thread Guy Steele


On Feb 15, 2022, at 5:58 PM, Remi Forax 
mailto:fo...@univ-mlv.fr>> wrote:




From: "Brian Goetz" mailto:brian.go...@oracle.com>>
To: "amber-spec-experts" 
mailto:amber-spec-experts@openjdk.java.net>>
Sent: Tuesday, February 15, 2022 7:50:06 PM
Subject: Re: Reviewing feedback on patterns in switch
We're preparing a third preview of type patterns in switch.  Normally we would 
release after a second preview, but (a) we're about to get record patterns, 
which may disclose additional issues with switch, so best to keep it open for 
at least another round, and (b) we're proposing some nontrivial changes which 
deserve another preview.

Here's where we are on these.


1.  Treatment of total patterns in switch / instanceof

Quite honestly, in hindsight, I don't know why we didn't see this sooner; the 
incremental evolution proposed here is more principled than where we were in 
the previous round; now the construct (instanceof, switch, etc) *always* gets 
first crack at enforcing its nullity (and exception) opinions, and *then* 
delegates to the matching semantics of the pattern if it decides to do so.  
This fully separates pattern semantics from conditional construct semantics, 
rather than complecting them (which in turn deprived users of seeing the model 
more clearly.)  In hindsight, this is a no-brainer (which is why we preview 
things.)  We'll be addressing this in the 3rd preview.

Not sure it's a no-brainer.
The question is more a question of consistency. There are two consistencies and 
we have to choose one, either switch never allows null by default and users 
have to opt-in with case null or we want patterns to behave the same way if 
they are declared at top-level or if they are nested. I would say that the 
semantics you propose is more like the current Java and the other semantics is 
more like the Java of a future (if we choose the second option).

I think we should try the semantics you propose and see if people agree or not.

And I agree we should try these semantics.


2.  Positioning of guards

Making guards part of switch also feels like a better factoring than making 
them part of patterns; it simplifies patterns and totality, and puts switch on 
a more equal footing with our other conditional constructs.  We did go back and 
forth a few times on this, but having given this a few weeks to settle, I'm 
pretty convinced we'd regret going the other way.

There were two sub-points here: (a) is the guard part of the pattern or part of 
switch, and (b) the syntax.  There was general agreement on (a), but some had 
preference for && on (b).  I spent some more time thinking about this choice, 
and have come down firmly on the `when` side of the house as a result for a 
number of reasons.

Still agree on (a)



 - Possibility for ambiguity.  If switching over booleans (which we will surely 
eventually be forced into), locutions like `case false && false` will be very 
confusing; it's pure puzzler territory.
 - && has a stronger precedence than keyword-based operators like 
`instanceof`'; we want guards to be weakest here.

I don't understand your point, we want instanceof pattern && expression to be 
equivalent to instanceof type && expression + cast, so the fact that && has a 
stronger precedence makes that possible so it's not an issue.

 - Using && will confuse users about whether it is part of the expression, or 
part of the switch statement.  If we're deciding it is part of the switch, this 
should be clear, and a `when` clause makes that clear.

I don't think it's that important, apart if we start to also want to combine 
patterns with &&


 - There are future constructs that may take patterns, and may (or may not) 
want to express guard-like behavior, such as `let` statements (e.g., let .. 
when .. else.)  Expressing guards here with && is even less evocative of "guard 
condition" than it is with switches.

It's not clear to me how to use "let when else". Is it more like a ?: in C than 
the let in in Caml ?

That is what I understood the implication to be: something like

let User(var firstname, var lastName) = x when firstName.length() > 8 in
System.out.printf(“User with long first name”);
else System.out.printf(“Not a user, or user with a short first name”);

although this particular example could also be framed as

if (x instanceof User(var firstname, var lastName) && firstName.length() > 8)
System.out.printf(“User with long first name”);
else System.out.printf(“Not a user, or user with a short first name”);

so maybe I am misunderstanding something here, or have misremembered the 
proposal.

 - Users coming from other languages will find `case...when` quite clear.
 - We've talked about "targetless" switches as a possible future feature, which 
express multi-way conditionals:

switch {
case when (today() == TUESDAY): ...
case when (location() == GREENLAND): ...
...
}

This would look quite silly with &&.

For me, this is l