| >>My example above demonstrates a constraint that cannot be implemented in a constructor. >It can't but that's not due to the proposed solution for withers. This has nothing to do with withers. It can't be implemented simply because it is a constraint on a specific transition of state. There's no transition of state in constructor. >Record are immutable on purpose and you want to add a mutation constraint whish will not play nicely. The purpose of immutability is not to create constant objects, but to prevent side effects from sharing mutable objects: state modifications are possible, they are simply reflected in a modified copy of object. That also means that my argument stands also for entities modeled as classes with final fields, it has nothing to do with specifics of records. Indeed, the fact that we have a constructor from which we can build any valid state and that we can deconstruct an immutable object means that we can bypass transition validations, but that will require some extra effort from developer and explicit demonstration of intent compared to simply using `with` block.
Compare this: var rA = new R(I1, I2, I3, A); // deserialization, e.g. from persistent state
// verbose, explicit intent to create a copy in state C
var rC = new R(rA.i1(), rA.i2(), rA.i3(), C); // error occurs later
this: // no semantics, no validation
var rC = rA with { state = C; } // error occurs later
and this: // clear semantics, validation of transition
var rC = rA.onSomethingHappened(C); // exception thrown now
|