Derived record creation and Data Oriented programming

forax at univ-mlv.fr forax at univ-mlv.fr
Tue Apr 30 15:35:36 UTC 2024


> From: "Brian Goetz" <brian.goetz at oracle.com>
> To: "Remi Forax" <forax at univ-mlv.fr>, "amber-spec-experts"
> <amber-spec-experts at openjdk.java.net>
> Sent: Tuesday, April 30, 2024 3:32:35 PM
> Subject: Re: Derived record creation and Data Oriented programming

> Interesting idea. Of the two sides, allowing explicit *constructor* calls is
> significantly more practical (`yield` has control flow consequences understood
> by the language, and obviously means "yield this value from the current
> expression", whereas doing a random pattern match with `this` as a match
> candidate is not remotely clear that you intend to override the
> deconstruction.)

> But, let's back up a second: what do we gain from this? Let's say we have two
> records with similar states:

> record A(int x, int y, int z) { }
> record B(int x, int y, int z) { }

> A a = ...
> B b = a with { yield new B(x, y, z); }

> I don't see how this is more clear than:

> B b = switch (a) {
> case A(var x, var y, var z) -> new B(x, y, z);
> };

> (or an imperative match, if we have one.) In fact, it seems less clear, since it
> is not really a "with" anything. It's using `with` as a short form of "shred to
> components", which is not what `with` is intended to convey.

> So let's back up: what problem are we trying to solve here?
The problem is that the syntax of "with" does not show that with depends on the deconstructor and the constructor of the record, so the behavior in case of separate compilation is not clear. 

I see several ways to fix that: 
- restrict the uses of "with" to the package/module containing the record (as we have done with sealed types), 
- allows a syntax variation where the components of the record are listed, 
- link the canonical deconstructor/constructor of the record at runtime (which can be simpler if the local variable declared outside of the block are captured (like the variables inside a "when" expression)). 

and maybe there are other solutions ? 

Rémi 

> On 4/30/2024 9:22 AM, Remi Forax wrote:

>> Hello,
>> they have been several messages on amber-dev about the compatibility of the
>> derived record creation.

>> I think part of the issue reported is that with the proposed syntax, the call to
>> the desconstructor and the canonical constructor is implicit.

>> Let's take an example

>>   record Point(int x, int y) {}

>>   var point = new Point(2, 3);

>> In fact,
>>   var point2 = point with { x = y; };

>> is a shortcut for:

>>   var point2 = point with {
>>     Point(int x, int y) = this;  // i.e. int x = this.x(); int y = this.y();
>>     x = y;
>>     yield new Point(x, y);
>>   };

>> One problem with the current syntax is that because the call to the
>> [de]constructors is implicit. I think we shoud allow users to write the
>> implicit calls if they want.

>> I wonder if
>> - we should not allow yield to be used so the compiler adds yield automatically
>> only if there is no yield ?
>> - we should in the future when deconstructors are introduced, allow users to
>> call a deconstructor explicitly and only provide one if not explicitly written
>> ?

>> Being able to write the calls explicitly is important because it's a way to
>> detect if the record has been modified without the proper
>> constructor/destructor has been written to be backward compatible (Like in a
>> switch, a record pattern detects when a record component is added while a type
>> pattern does not).

>> Being able to call a the deconstructor explicitly also have the advantage to
>> avoid to declare a variable/calls the accessor if not needed.
>> By example
>>   var point2 = point with {
>>     Point(_, var y) = this;
>>     x = y;
>>   };

>> regards,
>> Rémi
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-spec-observers/attachments/20240430/50386097/attachment-0001.htm>


More information about the amber-spec-observers mailing list