[External] : Re: Pattern assignment

Brian Goetz brian.goetz at oracle.com
Mon Mar 28 15:41:10 UTC 2022


>
> There are another different between assignment and _let_, a _let_ 
> creates new fresh local variables (binding) while assignment is able 
> to reuse an existing local variable.

Correct, the more precise analogy is not to _assignment_, but to _local 
variable declaration with initialization_ (whose semantics are derived 
from assignment.)

> In Java, the if statement is used a lot (too much IMO but i don't 
> think we should fight to change that) so it may make sense to be able 
> to reuse an existing local variables.

Yes, this has come up before.  I agree that there are cases where we 
might want this (there's one distinguished case where we almost cannot 
avoid this), but in general, I am pretty reluctant to go there -- I 
think this is incremental complexity (and encouragement of more 
mutability) with not enough commensurate benefit.

>
>
>
>     ## Possible extensions
>
>     There are a number of ways we can extend `let` statements to make
>     it more
>     useful; these could be added at the same time, or at a later time.
>
>     #### What about partial patterns?
>
>     There are times when it may be more convenient to use a `let` even
>     when we know
>     the pattern is partial.  In most cases, we'll still want to
>     complete abruptly if the
>     pattern doesn't match, but we may want to control what happens. 
>     For example:
>
>     ```
>     let Optional.of(var contents) = optName
>     else throw new IllegalArgumentException("name is empty");
>     ```
>
>     Having an `else` clause allows us to use a partial pattern, which
>     receives
>     control if the pattern does not match.  The `else` clause could
>     choose to throw,
>     but could also choose to `break` or `return` to an enclosing
>     context, or even
>     recover by assigning the bindings. 
>
>
> I don't like that because in that case "let pattern else ..." is 
> equivalent of "if instanceof pattern else ... " with the former being 
> expression oriented and the later statement oriented.
> As i said earlier, i don't think we should fight the fact that Java is 
> statement oriented by adding expression oriented variations of 
> existing constructs.

We haven't talked about let expressions yet; this is still a statement.

It's a fair point to say that the above example could be rewritten as an 
if-else, and when the else throws unconditionally, we still get the same 
scoping.  Or that it can be rewritten as

     if (!(pattern match))
         throw blah

On the other hand, people don't particularly like having to invert the 
match like this just to get the scoping they want.

In any case, the real value of the else block is where you want to 
continue (and merge the control flow) with default values of the 
bindings set in the else clause (next section).  Dropping "else" makes 
this extremely messy.  And once you have else, the rest comes for the ride.

>
>
>     #### What about recovery?
>
>     If we're supporting partial patterns, we might want to allow the
>     `else` clause
>     to provide defaults for the bindings, rather than throw.  We can
>     make the bindings of the
>     pattern in the `let` statement be in scope, but definitely
>     unassigned, in the
>     `else` clause, which means the `else` clause could initialize them
>     and continue:
>
>     ```
>     let Optional.of(var contents) = optName
>     else contents = "Unnamed";
>     ```
>
>     This allows us to continue, while preserving the invariant that
>     when the `let`
>     statement completes normally, all bindings are DA. 
>
>
> It fails if the "then" part or the "else" part need more than one 
> instruction.
> Again, it's statement vs expression.

No, it's still a statement.  I don't know where you're getting this 
"statement vs expression" thing from?

>
>
>     #### What about guards
>
>     If we're supporting partial patterns, we also need to consider the
>     case where
>     the pattern matches but we still want to reject the content.  This
>     could of
>     course be handled by testing and throwing after the `let`
>     completes, but if we
>     want to recover via the `else` clause, we might want to handle
>     this directly.
>     We've already introduced a means to do this for switch cases -- a
>     `when` clause
>     -- and this works equally well in `let`:
>
>     ```
>     let Point(var x, var y) = aPoint
>     when x >= 0 && y >= 0
>     else { x = y = 0; }
>     ```
>
>
> It can be re-written using an if instanceof, so i do not think we need 
> a special syntax
>
>   int x, y;
>   if (!(aPoint instanceof Point(_ASSIGN_ x, _ASSIGN_ y) && x >= 0 && y 
> >= 0)) {
>    x = 0;
>    y = 0;
>   }

All let statements can be rewritten as instanceof.  Are you arguing that 
the whole idea is silly?

>
> "Let ... in" is useful but i don't think it's related to the current 
> proposal, for me it's orthogonal. We can introduce "let ... in" 
> independently to the pattern assignment idea,
> and if the pattern assignment is already in the language, then "let 
> ... in" will support it.

Yes and no.  You are correct that we could do either or both 
independently.  But it's not my job just to design each feature in a 
locally optimal way; it's my job to ensure that the features we design 
will fit together when they meet up in the future.   The fact that the 
construct generalizes in this way is an important part of the design 
even if we don't plan to do this part now.



More information about the amber-spec-observers mailing list