Whilst I agree with Brian, is this not a smell though if it can't be restricted in this way, that the proposed additions aren't flexible enough to ensure program correctness in what's surely going to be a fairly common pattern?
Maybe withers shouldn't just be a syntactic optimisation for exactly this reason. -- Ryan On Mon, 26 Jan 2026, 5:40 am Brian Goetz, <[email protected]> wrote: > The important mental model here is that a reconstruction (`with`) > expression is "just" a syntactic optimization for: > > - destructure with the canonical deconstruction pattern > - mutate the components > - reconstruct with the primary constructor > > 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. > > Primary constructors can enforce invariants _on_ or _between_ components, > such as: > > record Rational(int num, int denom) { > Rational { if (denom == 0) throw ... } > } > > or > > record Range(int lo, int hi) { > Range { if (lo > hi) throw... } > } > > 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. 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 > > rec with { dbId++ } > > *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. > > 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. > > > > > On 1/25/2026 1:37 PM, Andy Gegg wrote: > > Hello, > 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. > > Since carrier classes and records are for data, in a database application > somewhere or other you're going to get database ids in records: > record MyRec(int dbId, String name,...) > > While everything is immutable this is fine but JEP 468 opens up the > possibility of mutation: > > MyRec rec = readDatabase(...); > rec = rec with {name="...";}; > writeDatabase(rec); > > which is absolutely fine and what an application wants to do. But: > MyRec rec = readDatabase(...); > rec = rec with {dbId++;}; > writeDatabase(rec); > > is disastrous. There's no way the canonical constructor invoked from > 'with' can detect stupidity nor can whatever the database access layer does. > > 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). > > Here there is nothing to say "do not update this!!!" except code comments, > JavaDoc and naming conventions. > > It's not always obvious which fields may or may not be changed in the > application. > > record MyRec(int dbId, int fatherId,...) > probably doesn't want > rec = rec with { fatherId = ... } > > but a HR application will need to be able to do: > > record MyRec(int dbId, int departmentId, ...); > ... > rec = rec with { departmentId = newDept; }; > > 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. > > 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. > > Thank you for your time, > Andy Gegg > > >
