Declared patterns -- translation and reflection
Remi Forax
forax at univ-mlv.fr
Tue Mar 29 22:19:27 UTC 2022
> From: "Brian Goetz" <brian.goetz at oracle.com>
> To: "amber-spec-experts" <amber-spec-experts at 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
More information about the amber-spec-observers
mailing list