Skip to main content

Is immutability very worthwhile when there is no concurrency? [Resolved]

It seems that thread-safety is always/often mentioned as the main benefit of using immutable types and especially collections.

I have a situation where I would like to make sure that a method will not modify a dictionary of strings (which are immutable in C#). I’d like to constrain things as much as possible.

However I am not sure whether adding a dependency to a new package (Microsoft Immutable Collections) is worth it. Performance is not a big issue either.

So I guess my question is whether immutable collections are strongly advised for when there are no hard performance requirements and there are no thread safety issues? Consider that value semantics (as in my example) might or might not be a requirement as well.


Question Credit: Den
Question Reference
Asked December 14, 2017
Posted Under: Programming
8 views
9 Answers

Immutability simplifies the amount of information you need to track mentally when reading code later. For mutable variables, and especially mutable class members, it's very hard to know what state they will be in at the specific line you're reading about, without running through the code with a debugger. Immutable data is easy to reason about - it will always be the same. If you want to change it, you need to make a new value.

I would honestly prefer making things immutable by default, and then changing them to mutable where it's proven that they need to be, whether this means you need the performance, or an algorithm you have doesn't make sense for immutability.


credit: KChaloux
Answered December 14, 2017

Your code should express your intention. If you don't want an object to be modified once created, make it impossible to modify.

Immutability has several benefits:

  • The intention of the original author is expressed better.

    How would you be able to know that in the following code, modifying the name would cause the application to generate an exception somewhere later?

    public class Product
    {
        public string Name { get; set; }
    
        ...
    }
    
  • It's easier to make sure that the object wouldn't appear in an invalid state.

    You have to control this in a constructor, and only there. On the other hand, if you have a bunch of setters and methods which modify the object, such controls may become particularly difficult, especially when, for example, two fields should change at the same time in order for the object to be valid.

    For example, an object is valid if the address is not null or GPS coordinates are not null, but it's invalid if both the address and the GPS coordinates are specified. Can you imagine the hell to validate this if both the address and GPS coordinates have a setter, or both are mutable?

  • Concurrency.

By the way, in your case, you don't need any third party packages. .NET Framework already includes a ReadOnlyDictionary<TKey, TValue> class.


credit: Arseni Mourzenko
Answered December 14, 2017

There are many single-threaded reasons to use immutability. For example

Object A contains Object B.

External code queries your Object B and you return it.

Now you have three possible situations:

  1. B is immutable, no problem.
  2. B is mutable, you make a defensive copy and return that. Performance hit but no risk.
  3. B is mutable, you return it.

In the third case the user code may not realize what you have done and may make changes to the object, and by so doing change the internal data of your object with you having no control or visibility of that happening.


credit: Tim B
Answered December 14, 2017

Immutability can also greatly simplify the implementation of garbage collectors. From GHC's wiki:

[...] Data immutability forces us to produce a lot of temporary data but it also helps to collect this garbage rapidly. The trick is that immutable data NEVER points to younger values. Indeed, younger values don't yet exist at the time when an old value is created, so it cannot be pointed to from scratch. And since values are never modified, neither can it be pointed to later. This is the key property of immutable data.

This greatly simplifies garbage collection (GC). At anytime we can scan the last values created and free those that are not pointed to from the same set (of course, real roots of live values hierarchy are live in the stack). [...] So it has a counter-intuitive behavior: the larger percent of your values are garbage - the faster it works. [...]


credit: Petr Pudlák
Answered December 14, 2017

Expanding on what KChaloux summarised very well...

Ideally, you have two types of fields, and hence two types of code using them. Either fields are immutable, and code doesn't have to take mutability into account; or fields are mutable, and we need to write code that either takes a snapshot (int x = p.x) or gracefully handles such changes.

In my experience, most code falls in between the two, being optimistic code: it freely references mutable data, assuming that the first call to p.x will have the same result as the second call. And most of the time, this is true, except when it turns out it no longer is. Oops.

So, really, turn that question around: What are my reasons for making this mutable?

  • Reducing memory alloc/free?
  • Mutable by nature? (e.g. counter)
  • Saves modifiers, horizontal noise? (const/final)
  • Makes some code shorter/easier? (init default, possibly overwrite after)

Do you write defensive code? Immutability will save you some copying around. Do you write optimistic code? Immutability will spare you the insanity of that weird, impossible bug.


credit: Community
Answered December 14, 2017

Another benefit of immutability is that it is the first step in rounding up these immutable objects into a pool. Then you can manage them so that you don't create multiple objects that conceptually and semantically represent the same thing. A good example would be Java's String.

It is a well-known phenomenon in linguistic that a few words appear a lot, might appear in other context as well. So instead of creating multiple String object, you can use one immutable. But then you need to keep a pool manager to take care of these immutable objects.

This will save you lot of memory. This is an interesting article to read as well: http://en.wikipedia.org/wiki/Zipf%27s_law


credit: InformedA
Answered December 14, 2017

In Java, C#, and other similar languages, class-type fields may be used either to identify objects, or to encapsulate values or state in those objects, but the languages make no distinction between such usages. Suppose a class object George has a field of type char[] chars;. That field may encapsulate a character sequence in either:

  1. An array which will never be modified, nor exposed to any code that might modify it, but to which outside references may exist.

  2. An array to which no outside references exist, but which George may modify freely.

  3. An array which is owned by George, but to which there may exist outside views which would be expect to represent George's current state.

Additionally, the variable may, instead of encapsulating a character sequence, encapsulate a live view to a character sequence owned by some other object

If chars presently encapsulates the character sequence [w i n d], and George wants chars to encapsulate the character sequence [w a n d], there are a number of things George could do:

A. Construct a new array containing the characters [w a n d] and change chars to identify that array rather than the old one.

B. Identify somehow a pre-existing character array which will always hold the characters [w a n d] and change chars to identify that array rather than the old one.

C. Change the second character of the array identified by chars to an a.

In case 1, (A) and (B) are safe ways to achieve the desired result. In case (2), (A) and (C) are safe, but (B) would not be [it wouldn't cause immediate problems, but since George would assume that it has ownership of the array, it would assume that it could change the array at will]. In case (3), choices (A) and (B) would break any outside views, and thus only choice (C) is correct. Thus, knowing how to modify the character sequence encapsulated by the field requires knowing which semantic type of field it is.

If instead of using a field of type char[], which encapsulates a potentially-mutable character sequence, code has used type String, which encapsulates an immutable character sequence, all of the above issues go away. All fields of type String encapsulate a sequence of characters using a sharable object that will never change. Consequently, if a field of type String encapsulates "wind", the only way to make it encapsulate "wand" is to make it identify different object--one which holds "wand". In cases where code holds the only reference to the object, mutating the object may be more efficient than creating a new one, but any time a class is mutable it is necessary to distinguish among the different ways by which it can encapsulate value. Personally I think Apps Hungarian should have been used for this (I would consider the four uses of char[] to be semantically-distinct types, even though the type system considers them identical--exactly the sort of situation where Apps Hungarian shines), but since it wasn't the easiest way to avoid such ambiguities is to design immutable types that only encapsulate values one way.


credit: supercat
Answered December 14, 2017

There is a lot of good answers already. This is just an extra info somewhat specific to .NET. I was digging through old .NET blog posts and found a nice sum-up of benefits from the point of view of the Microsoft Immutable Collections developers:

  1. Snapshot semantics, allowing you to share your collections in a way that the receiver can count on never changing.

  2. Implicit thread-safety in multi-threaded applications (no locks required to access collections).

  3. Any time you have a class member that accepts or returns a collection type and you want to include read-only semantics in the contract.

  4. Functional programming friendly.

  5. Allow modification of a collection during enumeration, while ensuring original collection does not change.

  6. They implement the same IReadOnly* interfaces that your code already deals with so migration is easy.

If someone hands you a ReadOnlyCollection, an IReadOnlyList, or an IEnumerable the only guarantee is that you can’t change the data – there is no guarantee that the person that handed you the collection won’t change it. Yet, you often need some confidence that it won’t change. These types don’t offer events to notify you when their contents change, and if they do change, might that occur on a different thread, possibly while you’re enumerating its contents? Such behavior would lead to data corruption and/or random exceptions in your application.


credit: Den
Answered December 14, 2017
Your Answer
D:\Adnan\Candoerz\CandoProject\vQA