4 ms·
One of the most frequently trumpeted cases for immutability is WRT thread safety. Is it me, or does this need come up rarely in practice relative to the forcefu
by unclebucknasty 11y ago
One of the most frequently trumpeted cases for immutability is WRT thread safety. Is it me, or does this need come up rarely in practice relative to the forcefulness of arguments to make everything immutable? I get that it depends on your project, etc. But, I'm talking overall.
There are other benefits to immutability, to be sure, but I appear to be the rare voice that finds the dictum to "make everything immutable" to be overstated. And, we seem to ignore the code bloat that comes along with it--builders and the like, as well as performance implications.
Of course we solve the challenges that mutability can introduce by lopping it off altogether, but at what cost? It's really not too far from solving problems that object misuse can introduce by getting rid of objects.
Replace all setters with builders, annotations, etc. Do not ever change an object's state. Ever! Run this Mutability Detector on your code to ensure you are not allowing something to be mutable. At all costs, kill those language features that allow object state to be changed!
At what point have we jumped the shark?
Maybe others believe in moderation as do I, and I just happen to be reading more of the forceful champions of immutability lately. But, they seem pretty loud and I have learned that edicts to never or always do something warrant great suspicion.
- pbh101 11y agoHaving worked in a couple different large Java projects, there are certainly tradeoffs to both sides: make too many things immutable and you will not end up in a happy place GC-wise. And add the requisite "it depends on the project" to the remainder of this comment :) But if I were to start a project, I would start with immutability by default and go from there, chiefly because when you find yourself needing to add additional threads and share data, I consider the transition from mutable to immutable would be more difficult than the inverse. Another perspective: mutability is a feature that can be used in certain contexts (and QUITE useful there) but nothing intrinsic to the feature (in Java, at least) enforces the contexts' prerequisites (i.e. thread-safe access), and thus it is easy to end up with runtime errors when the environment of your project shifts. Immutability, OTOH, makes fewer contextual assumptions (or perhaps more tractable ones). Said perhaps more pithily: when mutable-by-default, your resultant architecture will frequently hinge on mutability. If you instead start with immutability, the immutability will be less of a crutch than the mutability would have been.
- unclebucknasty 11y ago>nothing intrinsic to [mutability] (in Java, at least) enforces the contexts' prerequisites (i.e. thread-safe access) I guess that's where I differ: To my mind the mutability construct is not supposed to provide this. Instead, enforcing thread-safety should involve the use of concurrency constructs. And, that's the thing: OOP represents a stateful paradigm, in the sense that operations may be performed which mutate the state of an object. When we bring out the Hammer of Immutability to prevent potential concurrency issues, we are basically saying that we no longer want to be OO because of an unrelated requirement. Let's turn the objects into structs that can only be passed around and copied so we don't accidentally screw something up. This, rather than using the language's actual concurrency constructs to safely share data. >it is easy to end up with runtime errors when the environment of your project shifts I can't remember when this has happened to me and I'm genuinely having trouble seeing the case. If I have an object which suddenly must be shared across another thread, then I generally cease to access it on the initial thread without some sort of synchronization. So, I just don't see how you'd suddenly share data across threads under any model without very careful consideration of what's being shared and why. What operations will be performed with the data as input, what will be the output, and how/when do we communicate this to other threads? Once you've done that analysis, then you have a blueprint for how to safely share data the right way.
- danieldk 11y agoOne of the most frequently trumpeted cases for immutability is WRT thread safety. Is it me, or does this need come up rarely in practice relative to the forcefulness of arguments to make everything immutable? I get that it depends on your project, etc. But, I'm talking overall. Yes, I more than once had to fix code in an natural language processing library, because multi-threaded Jersey services led to either crashes or partial annotations literally ending up in the wrong request. In the cases where I had access to the code, I rewrote the classes to be immutable (all state is created and passed between method calls), which fixed it. In one natural language parser that we couldn't patch, I had to create one full parser instance per thread (weighing in at ~1-2GB), because the class was mutable and did not use correct locking. (So, we ended up creating a couple of parsers at startup and putting them in an object pool.)
- unclebucknasty 11y agoWhat you're describing are just plain bad coding practices by people who didn't understand concurrency. Proper locking and synchronization are the real fixes. Again, that's not to say that immutability never has a place.
- GFK_of_xmaspast 11y agoAs a C++ programmer, I make sure that things that can reasonably be const are const, because that reduces the number of places where things can go wrong. That said, sometimes state simply has to change, and anything else is just ideology.
- danieldk 11y agoConcurrency is hard. Immutability skates around the whole problem. A parser should be immutable, parsing state should be limited to a particular invocation of the parser. Of course, mutable state cannot be abolished everywhere (often for efficiency reasons). In such cases, you should indeed understand the concurrency primitives.
- lmm 11y agoSome language features are just bad and should be removed entirely. Moderation is often the answer, but not always. https://xkcd.com/690/ https://xkcd.com/690/ . > Of course we solve the challenges that mutability can introduce by lopping it off altogether, but at what cost? It's really not too far from solving problems that object misuse can introduce by getting rid of objects. At what cost then? I can point to specific scenarios where objects are useful and languages without them (e.g. Haskell) solve the problem less elegantly. Can you say the same for mutability? Have you got a specific business problem that you've solved with and without mutability and found that the mutability made it better? (FWIW my position is: no unmanaged mutability beyond method scope. Local mutability is fine, but anything that crosses method boundaries should be explicitly managed using e.g. State)
- unclebucknasty 11y agoFWIW if you advocate managed local mutability, then you also have folks who will tell you even that is evil. They would say internal mutability is still mutability. And, of course, it's true that you do also have to manage the same concurrency issues as with any mutability. >At what cost then? If a language offers a construct for achieving a certain goal, and you opt to bypass that construct to create a parallel construct, then you are necessarily creating additional cost. But, it's actually worse than that because it goes beyond creation of parallel constructs: it also includes working to actively suppress the built-in language constructs. And now we are creating immutability tools and new annotations, and running Mutability Detection, etc. as if mutability itself is a defect and stamping it out automatically improves code quality. >Have you got a specific business problem that you've solved with and without mutability Yes. Every bit of code I've written before the age of Mutability-is-Pure-Evil. That code worked well without the additional code bloat, object copying, and performance penalties associated with defeating natural language constructs. No annotations. No builders everywhere. Just pure, clean, intuitive code whereby other developers were required to actually understand the design before using it. You need to set a property? Then, set it. Don't construct a completely new object with a builder, just to update the zip code. In a multithreaded environment? Sorry, you need to understand concurrency issues. No amount of dumbing down objects is going to make up for your lack of understanding there. Otherwise, let's stop calling immutability OOP. What people really want is to pass around a struct and have procedural code wrapped in other objects perform operations on it. And, sorry. Moderation is key. Your use of "internal mutability" is actually a nod to moderation. When people find themselves repeating that something is always evil, it should trip a mental alarm. Sure, there are cases wherein immutability might be a reasonable way to solve a problem, but definitively stating that it is always the right approach is shark-jumping.