Having just watched one of Kevlin Henney's many talks on the subject on Youtube, I'm reminded of his mutability and shared state diagram. Since I can't find a copy of it outside of an hour long video, I'll try to replicate it here (sorry for those on mobile):
Mutable
^
(Good) | (Bad)
|
Non-Shared ----------> Shared
State | State
|
(Good) | (Good)
Immutable
On the top half of the graph you have Mutable state, on the bottom half Immutable. On the left you don't share the state and on the right you do. Everything is fine as long as you don't both mutate the state and share it. Of course, that's what we always feel like we want to do ;-)
Simple example: sometimes it's tremendously easier and more performant to write loops with an increment counter. Test it, stick it in a function, and it works just as well as any thing else.
I agree with you generally. However, using persistent data structures can sometimes be tricky and can complicate the code. As long as you contain the mutability you can get some benefits: increased performance, reduced memory usage, etc.
I once wrote a SIP client for Windows mobile in .Net. Resources were scarce and .Net would spin up threads if you so much as looked at it the wrong way. I had a single thread reading from the network into a circular queue -- clearly that had to be mutable or memory allocation alone would have set the machine on fire. I then used a reactor pattern in the UI thread: basically on the windows event loop being idle, I read from the circular queue. Because I could guarantee that there was never any more than 1 thread reading from the queue and 1 thread writing to the queue, I could get away with no locking (you drop packets that will overrun the queue).
For me, that's the kind of thing that this graph is saying: mutability is problematic, but if you are forced to use it you need to be really careful about concurrency.
I just mean I wouldn't refer to non-shared mutability as "good". You should aim for immutability by default and drop to mutability if it has a clear benefit which depending on the domain can be rare. Most of the time I'm optimising for developer productivity and reduction of bugs first; performance and memory consumption comes later if it's an issue.