<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  </head>
  <body>
    <font face="Times New Roman">Hello,<br>
      Thank you to all those who have spent time on this - I feel as if
      I poked something that needed to be poked.<br>
      <br>
      I know I talked about database ids which was a bad choice - as
      Brian has said, they shouldn't really be there!<br>
      <br>
      The point is that Records are lovely in so many ways that we're
      going to use them where they're *almost* right - and withers will
      make that easier and more appealing.  But because records have no
      setters there's no validation of the *change* except what the
      constructor can do - and that's intended for use where its
      parameter values have to be taken as correct (usually derived from
      a database, say), it cannot check a *change* is legal as it has no
      knowledge of previous values.  And so business logic starts to be
      implemented in front end code (in the with block), which is a Bad
      Thing.<br>
      <br>
      Thank you again for your time,<br>
      Andy Gegg</font><br>
    <br>
    <div class="moz-cite-prefix">On 25/01/2026 19:09, Brian Goetz wrote:<br>
    </div>
    <blockquote type="cite"
      cite="mid:a9015e64-3b0f-4ea2-883e-f3a93d079355@oracle.com">
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
      <font size="4" face="monospace">The important mental model here is
        that a reconstruction (`with`) expression is "just" a syntactic
        optimization for:<br>
        <br>
         - destructure with the canonical deconstruction pattern<br>
         - mutate the components <br>
         - reconstruct with the primary constructor<br>
        <br>
        So the root problem here is not the reconstruction expression;
        if you can bork up your application state with a reconstruction
        expression, you can bork it up without one.  <br>
        <br>
      </font><font size="4" face="monospace">Primary constructors can
        enforce invariants _on_ or _between_ components, such as:<br>
        <br>
            record Rational(int num, int denom) { <br>
                Rational { if (denom == 0) throw ... }<br>
            }<br>
        <br>
        or<br>
        <br>
            record Range(int lo, int hi) { <br>
                Range { if (lo > hi) throw... }<br>
            }<br>
        <br>
        What they can't do is express invariants between the record /
        carrier state and "the rest of the system", because they are
        supposed to be simple data carriers, not serialized references
        to some external system.  </font><font size="4" face="monospace">A
        class that models a database row in this way is complecting
        entity state with an external entity id.  By modeling in this
        way, you have explicitly declared that <br>
        <br>
            rec with { dbId++ }<br>
        <br>
        *is explicitly OK* in your system; that the components of the
        record can be freely combined in any way (modulo enforced
        cross-component invariants).  And there are systems in which
        this is fine!  But you're imagining (correctly) that this
        modeling technique will be used in systems in which this is not
        fine.  <br>
      </font><font size="4" face="monospace"><br>
        The main challenge here is that developers will be so attracted
        to the syntactic concision that they will willfully ignore the
        semantic inconsistencies they are creating.  <br>
        <br>
        <br>
      </font><br>
      <br>
      <div class="moz-cite-prefix">On 1/25/2026 1:37 PM, Andy Gegg
        wrote:<br>
      </div>
      <blockquote type="cite"
        cite="mid:512e9c0f-56d0-40cf-87d1-ec86a997165a@btinternet.com">
        <font face="Times New Roman">Hello,<br>
          I apologise for coming late to the party here - Records have
          been of limited use to me but Mr Goetz's email on carrier
          classes is something that would be very useful so I've been
          thinking about the consequences.<br>
          <br>
          Since  carrier classes and records are for data, in a database
          application somewhere or other you're going to get database
          ids in records:<br>
          record MyRec(int dbId, String name,...)<br>
          <br>
          While everything is immutable this is fine but JEP 468 opens
          up the possibility of mutation:<br>
          <br>
        </font><font face="Times New Roman">MyRec rec</font><font
          face="Times New Roman"> = readDatabase(...);<br>
          rec = rec with {name="...";};<br>
          writeDatabase(rec);<br>
          <br>
          which is absolutely fine and what an application wants to do. 
          But:<br>
        </font><font face="Times New Roman">MyRec </font><font
          face="Times New Roman">rec = readDatabase(...);<br>
          rec = rec with {dbId++;};<br>
          writeDatabase(rec);<br>
          <br>
          is disastrous.  There's no way the canonical constructor
          invoked from 'with' can detect stupidity nor can whatever the
          database access layer does.<br>
          <br>
          In the old days, the lack of a 'setter' would usually prevent
          stupid code - the above could be achieved, obviously, but the
          code is devious enough to make people stop and think (one
          hopes).<br>
          <br>
          Here there is nothing to say "do not update this!!!" except
          code comments, JavaDoc and naming conventions.<br>
          <br>
          It's not always obvious which fields may or may not be changed
          in the application.<br>
          <br>
          record </font><font face="Times New Roman">MyRec(int dbId, int
          fatherId,...)<br>
          probably doesn't want<br>
          rec = rec with { fatherId = ... }<br>
          <br>
          but a HR application will need to be able to do:<br>
          <br>
          record </font><font face="Times New Roman">MyRec(int dbId, int
          departmentId, ...);<br>
          ...<br>
          rec = rec with { </font><font face="Times New Roman">departmentId
          = newDept; };<br>
          <br>
          Clearly, people can always write stupid code (guilty...) and
          the current state of play obviously allows the possibility
          (rec = new MyRec(rec.dbId++, ...);) which is enough to stop
          people using records here but carrier classes will be very
          tempting and that brings derived creation back to the fore.<br>
          <br>
          It's not just database ids which might need restricting from
          update, e.g. timestamps (which are better done in the database
          layer) and no doubt different applications will have their own
          business case restrictions.<br>
          <br>
          Thank you for your time,<br>
          Andy Gegg<br>
        </font><br>
      </blockquote>
      <br>
    </blockquote>
    <br>
  </body>
</html>