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

Reply via email to