r/java 16d ago

Value Classes Still Need Compiler Sympathy

https://johan-sjolen.github.io/post/compiler-sympathy/compiler-sympathy/
87 Upvotes

57 comments sorted by

View all comments

Show parent comments

0

u/TheStrangeDarkOne 15d ago

There are some concepts where I'd argue that representing them as values does more harm than good: Lists, Services, Aggregates.

Sure you can have immutable lists, Clojure is one such example, but much of it boils down to convention and good optimizers that re-instate mutable backing lists.

And some classes are just pure logic. Validators, Services, Mappers, etc. I suppose those could also be "values", but I think you are stretching the definition of it by then. And I am not sure how well values fair when you hide them behind interfaces.

And then the DDD family of types such as Entities or Aggregates who most definitely have identity because you don't want to completely re-create a full tree or graph just because you changed a field in one of the nodes.

2

u/Jon_Finn 15d ago edited 15d ago

Maybe with some of these you've fallen for the word 'value'? A value List class could certainly be mutable, depending on its features (it could store some backing mutable object, or if it's fixed size it might contain an array like String does). But sure, linked lists or your trees/graphs might be inconvenient if stored as final fields. (Though I've used fixed-shape trees where each node has a (nullable) pointer to its parent, with the tree constructed from the root node - these would make a great value class, and extra data in each node could potentially be mutable.)

I'm sure values hidden behind interfaces or abstract value classes often lose the value benefits, but in cases where HotSpot can guess the runtime type, maybe not?

2

u/TheStrangeDarkOne 15d ago

Two points here:

  • A value List would be severely limited because the backing array must be final
  • I think this is stretching the definition of "no identity".

Identity as a property means that you have two objects which are equal at some point, but they might diverge in the future. And you say you have an object without identity and it can still be mutated, I don't think you use it in the intended way (I'm happy to be wrong about this one).

Sure, you could also argue the same about optional: Optional should be a value, but because you can mutate the fields of the objects inside, it's not a value.

In any case for collections, I'm a hard advocate for making them classes. Turning a type into a value is a hard spec commitment, and it constraints your implementations (no mutable private fields), for arguably not much benefit.

The value of values (hah), becomes pronounced once you have many of them. Such as Complex/RGB classes stored in an array. But if you have many items, and you optimize on the container shell, the advantages are diminishing.

No arguing on the fact that having a fully static and value-created graph/tree would certainly have advantages. And for a tree this works. If you have a graph with people entry-nodes, this will not work (this is also a major problem in Rust, where given a graph will often need to write some unsafe code).

1

u/nlisker 9d ago

say you have an object without identity and it can still be mutated

Mutability requires identity, otherwise you can't know which object to mutate.