Data Oriented Programming, Beyond Records
forax at univ-mlv.fr
forax at univ-mlv.fr
Wed Jan 21 18:36:16 UTC 2026
> From: "Brian Goetz" <brian.goetz at oracle.com>
> To: "Remi Forax" <forax at univ-mlv.fr>
> Cc: "Viktor Klang" <viktor.klang at oracle.com>, "amber-spec-experts"
> <amber-spec-experts at openjdk.java.net>
> Sent: Wednesday, January 21, 2026 7:01:52 PM
> Subject: Re: Data Oriented Programming, Beyond Records
>> The other solution seems to allow a carrier class to not be restricted to only
>> describe data so like a record equals/hashCode/toString are derived from the
>> fields that define a component.
> I'd like to stay away from the "macro generator" view of the world, and instead
> talk about semantics and conceptual models. The more we describe things in
> terms of macro generation, the less users will be able to _see_ the underlying
> concepts.
> I believe I had already anticipated and preemptively dismissed the model you
> outline below, but I'm sure it was easy to miss. But here's the problem:
> `component` fields are supposed to be implementation details of _how_ a carrier
> class achieves the class contract. Having the semantics of equality depend on
> which fields are component fields has several big problems:
> - It means that clients can't reason about what equality means by looking at the
> class declaration.
> - It means that the behavior of a class will change visibly under
> harmless-looking implementation-only refactorings, such as migrating a field
> from a component field to some other representation.
yes, both are true, this is also true for any classes in general.
> - Having flung open the door labeled "macro generator", users will instantly
> demand "I want this to be a component field to get the accessor and constructor
> boilerplate, but I don't want it part of equals, can I please have a modifier
> to condition what counts in equals and hashCode, #kthxbye". This violates Rule
> Number One of the record design, which is "no generation knobs, ever". Once you
> have one, you are irrevocably on the road to Lombok.
I think this is true for any proposals that generate code for implementing a carrier class, including the one you propose before.
When designing records, we have established that the only reasonable proposition that can not be labelled as a code generator is ... a record.
> So there are two stabled, principled alternatives:
> - Just don't ever try to derive equals and hashCode
> - Derive equals and hashCode similarly as for records
> And of course, the first means that records cannot be considered special cases
> of carriers. So the latter seems a forced move.
I still think there is value to consider that a carrier class is an abstract specification that just say that it can be constructed/deconstructed but equals/hashCode are user defined.
When you use only pattern matching, you do not really need equals/hashCode to be defined, you recursively pattern-match until you have primitive values.
> (Meta observation: it is easy, when you have your Code Generator hat on, to
> suggest reasonable-seeming things that turn out to be semantically questionable
> or surprising or inconsistent; this is why we try so hard to approach this
> semantics-first.)
Rémi
>> The carrier class feature:
>> A carrier class is a class that define "virtual" components
>> - the accessors must be implemented or declared abstract
>> - the canonical constructor must be implemented if the class is concrete
>> - the record pattern works using accessors.
>> A carrier class is not restricted to be a data class (so no
>> equals/hashCode/toString), it can represent any class that can be
>> constructed/deconstructed as several virtual components.
>> The component field feature:
>> A field can be declared as "component", i.e. implementing one of the virtual
>> component
>> - the field is automatically strict
>> - the accessor corresponding to the field can be derived (if not @Override)
>> - if at least one of the field is declared as component,
>> equals/hashCode/toString can be derived
>> - if at least one of the field is declared as component, the compact constructor
>> syntax is available and inside it, all the fields marked as component are
>> initialized automatically
>> - if all virtual components have an associated component field, the canonical
>> constructor can be derived
>> regards,
>> Rémi
>>> On 1/20/2026 1:35 PM, [ mailto:forax at univ-mlv.fr | forax at univ-mlv.fr ] wrote:
>>>>> From: "Brian Goetz" [ mailto:brian.goetz at oracle.com | <brian.goetz at oracle.com> ]
>>>>> To: "Remi Forax" [ mailto:forax at univ-mlv.fr | <forax at univ-mlv.fr> ]
>>>>> Cc: "Viktor Klang" [ mailto:viktor.klang at oracle.com | <viktor.klang at oracle.com>
>>>>> ] , "amber-spec-experts" [ mailto:amber-spec-experts at openjdk.java.net |
>>>>> <amber-spec-experts at openjdk.java.net> ]
>>>>> Sent: Tuesday, January 20, 2026 4:28:14 PM
>>>>> Subject: Re: Data Oriented Programming, Beyond Records
>>>>>>>> The modifier "component" is too close to the "property" modifier I wanted to
>>>>>>>> include years ago, it's just to sugary for its own good.
>>>>>>> You know the rule; mention syntax and you forfeit the right to more substantial
>>>>>>> comments....
>>>>>> I'm talking about the semantics, especially the fact that equals/hashCode and
>>>>>> toString() are derived from.
>>>>> Except that equals/hashCode have nothing to do with the "component" modifier _at
>>>>> all_. They are derived from the _state description_, in terms of the
>>>>> _accessors_, whose existence is implied directly by the _state description_.
>>>> I do not think you can do that, because it means that a record is not a carrier
>>>> class.
>>>> Do you agree that equals() and hashCode() of a record are not derived from the
>>>> accessors ?
>>>> regards,
>>>> Rémi
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-spec-experts/attachments/20260121/7f77568d/attachment.htm>
More information about the amber-spec-experts
mailing list