1 of 8

I am admittedly biased as one applying such concepts in C++ by the language and its nature, as well as my domain, and even the way we use the language. But given these things, I think immutable designs are the least interesting aspect when it comes to reaping a bulk of the benefits associated with functional programming, like thread safety, ease of reasoning about the system, finding more reuse for functions (and finding we can combine them in any order without unpleasant surprises), etc.

Take this simplistic C++ example (not optimized for simplicity):

// Inputs an image and outputs a new one with the specified size.
Image resized_image(const Image& src, int new_w, int new_h)
{
     Image dst(new_w, new_h);
     for (int y=0; y < new_h; ++y)
     {
         for (int x=0; x < new_w; ++x)
              dst[y][x] = src.sample(x / (float)new_h, y / (float)new_h);
     }
     return dst;
}

While the implementation of that function mutates local (and temporary) state in the form of two counter variables and a temporary local image to output, it has no external side effects. It inputs an image and outputs a new one. We can multithread it to our hearts' content. It's easy to reason about, easy to thoroughly test. It's exception-safe since if anything throws, the new image is tossed away.

I see little to be gained, and potentially much to be lost, by making Image immutable in the above context, in C++, except to potentially make the above function more unwieldy to implement, and possibly a bit less efficient.

Purity

So pure functions (free of external side effects) are very interesting to me, and I emphasize the importance of favoring them often to team members even in C++. But immutable designs, applied just generally absent context and nuance, are not nearly as interesting to me since, given the imperative nature of the language, it's often useful and practical to be able to mutate some local temporary objects in the process of efficiently (both for developer and hardware) implementing a pure function.

Cheap Copying of Hefty Structures

The second most useful property I find is the ability cheaply copy the really hefty data structures around when the cost of doing so, as would often be incurred to make functions pure given their strict input/output nature, would be non-trivial. These wouldn't be small structures that can fit on the stack. They'd be big, hefty structures, like the entire Scene for a video game.

In that case the copying overhead could prevent opportunities for effective parallelism, because it might be difficult to parallelize physics and rendering effectively without locking if physics is mutating the scene that the renderer is simultaneously trying to draw. However, if the physics system was 'pure' in the sense that it merely inputted a scene and outputted a new one with physics applied, it could safely operate in parallel with the renderer, without one waiting on the other.

So the ability to cheaply copy the really hefty data of your application state around and output new, modified versions with minimal cost to processing and memory use can really open up new doors for effective parallelism, and there I find lots of lessons to learn from how persistent data structures are implemented.

Immutability

Finally there's immutability which I consider the least interesting of these three, but it can enforce, with an iron fist, when certain object designs are not meant to be used as local temporaries to a pure function, a valuable kind of "object-level purity", as in all methods no longer cause external side effects (no longer mutate member variables outside the immediate local scope of the method).

And while I consider it the least interesting of these three in languages like C++, it can certainly simplify the testing and thread-safety and reasoning of non-trivial objects. It can be a load off to work with the guarantee that an object cannot be given any unique state combination outside of its constructor, for example, and that we can freely pass it around, even by reference/pointer without leaning on constness and read-only iterators and handles and such, while guaranteeing (well, at least as much as we can within the language) that its original contents will not be mutated.

But I find this the least interesting property because most objects I see as beneficial as being used temporarily to implement a pure function, and I think immutability to the extremities in an immutable language is a rather counter-productive goal. I'd apply it sparingly for the parts of the codebase where it really helps the most.