4 ms·
This isn't about immutability, this is about the power of introducing value objects to a system. (Immutability is just a property of good value objects.) Most
by jdlshore 10y ago
This isn't about immutability, this is about the power of introducing value objects to a system. (Immutability is just a property of good value objects.)
Most code I read does not use value objects. It's more common for me to see primitive obsession [1], data clumps [2], arbitrary structs, or the "struct + service" antipattern (looking at you, Angular 1). Granted, I'm not working with FP languages.
When you migrate code to use value objects, then follow up by moving associated behavior into the value object, there are significant knock-on benefits to your design.
Value objects may be simple, but they're not obvious. If they were, more code would use them.
[1] http://www.jamesshore.com/Blog/PrimitiveObsession.html http://www.jamesshore.com/Blog/PrimitiveObsession.html
[2] http://www.martinfowler.com/bliki/DataClump.html http://www.martinfowler.com/bliki/DataClump.html
- Silhouette 10y agoValue objects may be simple, but they're not obvious. If they were, more code would use them. To the extent that the concept is relevant, the difference is obvious unless you only ever use languages that are ambiguous about reference/value semantics. If you use a language like C or C++ or even assembly, where pointers and taking an address are explicit and fundamental concepts, there's mostly no problem. Your data is a value by default, and if you want a reference (pointer) instead, you just make one, explicitly. The exception is arrays, and in not entirely unrelated news, array semantics in C and C++ are a significant source of bugs. If you use a functional programming language, again data is typically immutable unless you explicitly use some sort of reference type, so again no problem. I have long maintained that one of the worst of Java's many design flaws was using the exact same syntax to represent two distinct but related concepts, depending on whether you were referring to a primitive or a class-based type. At least with the static typing you can see which you've got, though. I can't fathom why you'd deliberately design a language to work like JS or Python, where not only can the exact same syntax mean two completely different things but which you get depends on dynamic typing information that you can't even see until runtime, or even on the exact values you're using rather than their types, in a case like Python's handling of small integers. I shudder to think how much productivity has been lost by now to bugs caused by mixing up .equals() with == in Java, == with === in JS, and == with `is` in Python.
- lorddoig 10y ago> Immutability is just a property of good value objects. It sounds rather like value objects are strictly superior than immutable data structures, based on this statement. I'd love to hear an expansion of this rationale.
- encoderer 10y agoAn immutable value object provides a richer contract. An immutable data structure like a tuple is going to provide a guarantee the data hasn't changed, but it doesn't provide a guarantee of what the data is.
- int_19h 10y agoIt's actually the other way around. If your object is immutable, whether it has identity or not (i.e. whether it's a "value object" or not) doesn't matter! There's nothing useful you could derive from that identity, so it might as well not exist. So, languages should stop thinking in terms of "this is a reference type" and "this is a value type", and start thinking about "this references something mutable" vs "this references something immutable". For the latter, the implementation can then use by-value semantics for perf reasons, where appropriate.
- comex 10y agoNot sure what you mean. Identity comparisons are more useful for mutable objects, but even an immutable object can use one as an alternative to a unique ID. For example, you might have a queue of input events, where once an event is posted nothing about it needs to change, but multiple events with the same properties are possible (e.g. pressing the same key twice in a row). On the other hand, mutable objects with value semantics can provide the ergonomic advantage that mutation has in some cases (e.g. 'point.y += 10' rather than 'point = Point(point.x, point.y + 10)' as well as more predictable performance, while avoiding bugs caused by accidental aliasing.
- louthy 10y ago> point.y += 10 How is that having value semantics? Or are you just referring to equality testing? Even then you're playing a dangerous game if you have multiple threads (one mutating, one running an equality check) and aren't using locks.