There is a discussion about whether ValueObjectsShouldBeImmutable or ValueObjectsCanBeMutable.
But the truth is, ValueObjectsShouldBePassedByValue, and that's all we need to know. As soon as you pass an object by value, there is no longer any logical distinction between mutable and immutable ones. The discussion becomes irrelevant. Here's why.
Consider a pass-by-value Point object:
struct Point { public int x; public int y; }Defined this way, it would be considered "mutable". The statement "myPoint.x += 5;" would be considered mutating the object.
Consider another implementation of the Point object:
struct Point { private int _x; private int _y; public Point(int x, int y) { _x = x; _y = y; } public int getX() { return _x; } public int getY() { return _y; } public Point move(int byX, int byY) { return new Point(_x + byX, _y + byY); } }Defined this way, the type would be considered "immutable". The statement "myPoint = myPoint.move(5, 0);" would be considered to be returning a new object and discarding the old.
But logically speaking, what really happens is exactly the same. The contents of myPoint were (x, y) and become (x+5, y). There is no semantic difference between the two operations.
This reasoning works because in a pass-by-value type, there is only a single memory location on which you can operate. There are no multiple references to the same object which could have their assumption of being "immutable" violated by a statement like "myPoint.x += 5;".
While I agree that in theory a ValueObject should be passed by value (hence the name), the performance impact is so huge that I wouldn't even consider it for non-trivial objects.
Of course, "huge performance impact" is relative; for some applications, it will be unnoticeable. CopyOnWrite mechanisms can reduce the performance impact of PassByValue, but it is unfortunate that most popular OO languages make efficient use of ValueObjects rather awkward.
There's a huge performance impact for 'mutable' concepts, too... you just don't see these costs until you start ratcheting up parallelism and closures, or attempt to achieve certain forms of PartialEvaluation at CompileTime, or attempt to automate caching and DataDeltaIsolation in FunctionalReactiveProgramming. The performance cost of mutation is so huge that I feel disgust at merely considering mutation for non-trivial projects.
Mutation might seem high-performance on the small-scale. On the large scale, it's horrible for performance.
Anyhow, I believe that 'EverythingIsAnObject' is the culprit here. Languages should simply provide FirstClass support for 'immutable values', especially including large collection values with substructure sharing, and SideEffect-free functions over values.