Looking ahead: pattern assignment

Brian Goetz brian.goetz at oracle.com
Fri Mar 12 19:58:40 UTC 2021


While this is not on the immediate horizon, I think we are ready to put 
the pieces together for pattern assignment.  I think we now understand 
the form this has to take, and the constraints around it.

Just as we were successfully able to unify pattern variables with 
locals*, I would like to be able to unify pattern assignment with 
assignment.

A pattern assignment takes the form:

     P = e

where P is a pattern with at least one binding variable that is total 
(perhaps with remainder) on the type of e.  (If P has some remainder on 
the type of e, then the assignment will throw NPE or ICCE.)  All 
bindings in P are in scope and DA for the remainder of the block in 
which P appears, just as with local variable declaration.

Pattern assignment should work in all of the following contexts:

  - Assignment statements: P = e
  - foreach-loops: for (P : e) { ... }
  - (optional) try-with-resources: try (P = e) { ... }
  - (optional) method formals: void m(Point(var x, var y) p) { ... }
  - (optional) lambda formals: (Point(var x, var y) p) -> { ... }

(And I'm sure I forgot some.)

Minimally, we have to align the semantics of local variable declaration 
with assignment with that of pattern matching; `T t = e` should have the 
same semantics whether we view it as a local declaration plus 
assignment, or a pattern match.  This means that we have to, minimally, 
align the assignment-context conversions in JLS 5.  (If we wish to 
support patterns in method/lambda formals, we also have to align the 
method-invocation context conversions.)

Early in the game, we explored supporting partial patterns in pattern 
assignment, such as:

     let P = e
     else { ... }

where the `else` clause must either complete abruptly, or assign to all 
bindings declared in `P`.  (It wasn't until we unified pattern variables 
with locals that there was an obvious way to specify the latter.)  While 
this construct is sound, it is in tension with other uses of pattern 
assignment:

  - (syntactic) Its pretty hard to imagine an `else` clause without 
introducing the assignment with some sort of keyword, such as `let`, but 
this limits its usefulness in other contexts such as method parameter 
declarations;
  - (pragmatic) It just doesn't add very much value; if the else throws, 
it is no less verbose than an if-else.

The remaining case where this construct helps is when we want to assign 
default values:

     let Point(var x, var y) = aPoint
     else { x = y = 0; }
     // can use x, y here either way

But, I think we can get there another way, by letting patterns bind to 
existing variables somehow (we want something like this for the analogue 
of super-delegation and similar in pattern declarations anyway.)  I 
won't paint that bikeshed here, except to suggest that the let-else 
construct seems to be a losing price-performance proposition.

I suspect the right time to formalize pattern assignment is when we 
formalize deconstructor declarations (probably next round). In the 
meantime, we should:

  - gather a complete list of contexts where pattern assignment makes sense;
  - nail down semantics of primitive type patterns (see earlier mail);
  - think about how to align the conversion rules in JLS 5 to align with 
existing usage.




*the only remaining difference between pattern variables and locals is 
that pattern variables have a more interestingly-shaped scope (and 
perhaps in the future, pattern variables may have multiple declaration 
points in the presence of OR patterns / merging via ORing of boolean 
expressions)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.java.net/pipermail/amber-spec-experts/attachments/20210312/3e8573c1/attachment-0001.htm>


More information about the amber-spec-experts mailing list