<html><body><div style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000"><div><br></div><div><br></div><hr id="zwchr" data-marker="__DIVIDER__"><div data-marker="__HEADERS__"><blockquote style="border-left:2px solid #1010FF;margin-left:5px;padding-left:5px;color:#000;font-weight:normal;font-style:normal;text-decoration:none;font-family:Helvetica,Arial,sans-serif;font-size:12pt;"><b>From: </b>"Brian Goetz" <brian.goetz@oracle.com><br><b>To: </b>"Remi Forax" <forax@univ-mlv.fr><br><b>Cc: </b>"Viktor Klang" <viktor.klang@oracle.com>, "amber-spec-experts" <amber-spec-experts@openjdk.java.net><br><b>Sent: </b>Wednesday, January 21, 2026 8:54:40 PM<br><b>Subject: </b>Re: Data Oriented Programming, Beyond Records<br></blockquote></div><div data-marker="__QUOTED_TEXT__"><blockquote style="border-left:2px solid #1010FF;margin-left:5px;padding-left:5px;color:#000;font-weight:normal;font-style:normal;text-decoration:none;font-family:Helvetica,Arial,sans-serif;font-size:12pt;"><blockquote cite="mid:626263129.22463743.1769024919495.JavaMail.zimbra@univ-eiffel.fr">
      <div style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000">
        <div>
          <blockquote style="border-left:2px solid #1010FF;margin-left:5px;padding-left:5px;color:#000;font-weight:normal;font-style:normal;text-decoration:none;font-family:Helvetica,Arial,sans-serif;font-size:12pt;">But
            also, you pay a big complexity tax when the new concept is
            almost like but can't quite fully meet up with the old
            concept; it again means that refactoring from record
            <--> carrier comes with a significant sharp edge, and
            this is a big warning signal.  So we would need a much
            stronger reason than "hmm, kind of like it better this way"
            to choose this divergent path.  So far I'm not seeing it?</blockquote>
          <div><br>
          </div>
          <div>There is a big sharp edge between a record and a class
            which is due to mutability, this is true inside the class
            but also outside, the user code when something is mutable or
            not is quite different .So refactoring from a mutable class
            to to an unmodifiable record is a not battle we should be
            interested in.</div>
        </div>
      </div>
    </blockquote>
    <br>
    Just because carrier class state _can_ be mutable, doesn't mean it
    _must_ be.  So you're skipping over the interesting case, which is:<br>
    <br>
        record R(int x, ...) { }<br>
    <br>
    and<br>
    <br>
        final class R(int x, ...) { private final component int x; ...
    }  // not equivalent to above!  <br>
    <br>
    In your model, the class version of R is painful to write, because
    you have to write equals, hashCode, and toString that delegate to
    each of the components.  </blockquote><div><br data-mce-bogus="1"></div><div>If you want to model something mutable you have to maintain the invariants, if you want to have some fields to be component, you have to write equals/hashCode/toString.</div><div><br data-mce-bogus="1"></div><div>We can provide a more declarative syntax:</div><div>- for the former, the syntax can declare the preconditions and for each field how to do the defensive copy</div><div>- for the latter, the syntax can declare each field that are part of the equality dance so equals/hashCode/toString can be derived.</div><div><br data-mce-bogus="1"></div><div>A record is easier to write because it's a sum-types, so it's unmodfiable *and* you can derived equals/hashCode/toString, that's the sweet spot.</div><div><br data-mce-bogus="1"></div><blockquote style="border-left:2px solid #1010FF;margin-left:5px;padding-left:5px;color:#000;font-weight:normal;font-style:normal;text-decoration:none;font-family:Helvetica,Arial,sans-serif;font-size:12pt;">But that's not even the main point; it is
    that while there is no theoretical distinction between a record and
    a final class all of whose components are backed by final component
    fields, there is a big and hard-to-explain discontinuity when you
    start from either of those and try to refactor to the other.</blockquote><div><br></div><div>You are focusing on the gap between an unmodifiable class and a record, even if we fill that gap by adding the "component" feature,</div><div>the gap between modifiable and unmodifiable will still exist. And for me, i do not see why one gap is more important than the other.</div><div><br data-mce-bogus="1"></div><blockquote style="border-left:2px solid #1010FF;margin-left:5px;padding-left:5px;color:#000;font-weight:normal;font-style:normal;text-decoration:none;font-family:Helvetica,Arial,sans-serif;font-size:12pt;">  <br>
    <br>
    But you are still not justifying your preference; WHY is
    identity-based equality the *obviously right* choice for carriers? 
    Be semantic please!  Tell me what you think a carrier *means*.  </blockquote><div><br data-mce-bogus="1"></div><div>For an enum, the semantics of equals() has to be ==, so a carrier enum should use the identity-based semantics.</div><div>For a data class, the semantics of equals is likely to not be == so a carrier data class has to override equals/hashCode.</div><div><br data-mce-bogus="1"></div><div><div>Basically, a carrier class does not take a side on what the semantics of equals should be, both are fine depending on the use case.</div></div><div><br></div><div>regards,</div><div>RĂ©mi</div><div><br data-mce-bogus="1"></div></div></div></body></html>