I am disappointed that you took this as an invitation to digress into
syntax here, when it should have been blindingly obvious that this was
not the time for a syntax discussion. (And when there is a syntax
discussion, which this isn't, we need to cover all the different forms
of declared patterns together; trying to design dtor patterns in a
vacuum misses a number of considerations.)
I'll respond to your other points separately.
On 3/29/2022 6:19 PM, Remi Forax wrote:
------------------------------------------------------------------------
*From: *"Brian Goetz" <brian.go...@oracle.com>
*To: *"amber-spec-experts" <amber-spec-experts@openjdk.java.net>
*Sent: *Tuesday, March 29, 2022 11:01:18 PM
*Subject: *Declared patterns -- translation and reflection
Time to take a peek ahead at _declared patterns_. Declared
patterns come in three varieties -- deconstruction patterns,
static patterns, and instance patterns (corresponding to
constructors, static methods, and instance methods.) I'm going to
start with deconstruction patterns, but the basic game is the same
for all three.
I mostly agree with everything said apart from the syntax of a
deconstructor
(see my next message about how small things can be improved).
I have several problems with the proposed syntax for a deconstructor.
I can see the appeal of having a code very similar to a constructor
but it's a trap, a constructor and a deconstructor do not have the
same semantics, a constructor initialize fields (which have a name)
while a deconstructor (or a pattern method) initialize bindings which
does not have a name at that point yet.
1/ conceptually there is a mismatch, the syntax introduce names for
the bindings, but they have no names at that point, bindings only have
names AFTER the pattern matching succeed.
2/ sending the value of the binding by name is alien to Java. In Java,
sending values is by the position of the value.
3/ the conceptual mismatch also exists at runtime, you need to permute
the value of bindings before creating the carrier because a carrier
takes the value of the binding by position while the code will takes
the value of the bindings by name (you need the equivalent of
MethodHandles.permuteArguments() otherwise you will see the
re-organisation of the code if they are side effects).
Let's try to come with a syntax,
as i said, bindings have no names at that point so the deconstructor
should declare the bindings (int, int) and not (int x, int y),
so a syntax like
_deconstructor_ (int, int) {
_send_bindings_(this.x, this.y);
}
Here the syntax shows that the value of the bindings are assigned
following the position of the expression like usual in Java.
We can discuss if _send_bindings_ should be "return" or another
keyword and if the binding types should be declared before or after
_deconstructor_.
By example, if you wan to maintain a kind of symmetry with the
constructor, we can reuse the name of the class instead of
_deconstructor_ and move the binding types in front of the name of the
class to show that the bindings move from the class to the pattern
matching in the same direction like a return type of a method.
Something like this:
(int, int) Point {
_send_bindings_(this.x, this.y);
}
To summarize, the proposed syntax does the convey the underlying
semantics of the bindings initialization and make things more
confusing than it should.
Ignoring the trivial details, a deconstruction pattern looks like
a "constructor in reverse":
```{.java}
class Point {
int x, y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
deconstructor(int x, int y) {
x = this.x;
y = this.y;
}
}
```
Rémi