63

I've read a couple of articles on immutability but still don't follow the concept very well.

I made a thread on here recently which mentioned immutability, but as this is a topic in itself, I am making a dedicated thread now.

I mentioned in the past thread that I thought immutability is the process of making an object read only and giving it low visibility. Another member said it didn't really have anything to do with that. This page (part of a series) uses an example of an immutable class/struct and it uses readonly and other concepts to lock it down.

What exactly is the definition of state in the case of this example? State is a concept which I haven't really grasped.

From a design guideline perspective, an immutable class must be one which does not accept user input and really would just return values?

My understanding is that any object which just returns information should be immutable and "locked down", right? So if I want to return the current time in a dedicated class with that one method, I should use a reference type as that will work a reference of the type and thus I benefit from immutability.

David Pokluda
  • 10,693
  • 5
  • 28
  • 26
GurdeepS
  • 65,107
  • 109
  • 251
  • 387

15 Answers15

61

What is Immutability?

  • Immutability is applied primarily to objects (strings, arrays, a custom Animal class)
  • Typically, if there is an immutable version of a class, a mutable version is also available. For instance, Objective-C and Cocoa define both an NSString class (immutable) and an NSMutableString class.
  • If an object is immutable, it can't be changed after it is created (basically read-only). You could think of it as "only the constructor can change the object".

This doesn't directly have anything to do with user input; not even your code can change the value of an immutable object. However, you can always create a new immutable object to replace it. Here's a pseudocode example; note that in many languages you can simply do myString = "hello"; instead of using a constructor as I did below, but I included it for clarity:

String myString = new ImmutableString("hello");
myString.appendString(" world"); // Can't do this
myString.setValue("hello world"); // Can't do this
myString = new ImmutableString("hello world"); // OK

You mention "an object that just returns information"; this doesn't automatically make it a good candidate for immutability. Immutable objects tend to always return the same value that they were constructed with, so I'm inclined to say the current time wouldn't be ideal since that changes often. However, you could have a MomentOfTime class that is created with a specific timestamp and always returns that one timestamp in the future.

Benefits of Immutabilty

  • If you pass an object to another function/method, you shouldn't have to worry about whether that object will have the same value after the function returns. For instance:

    String myString = "HeLLo WoRLd";
    String lowercasedString = lowercase(myString);
    print myString + " was converted to " + lowercasedString;
    

    What if the implementation of lowercase() changed myString as it was creating a lowercase version? The third line wouldn't give you the result you wanted. Of course, a good lowercase() function wouldn't do this, but you're guaranteed this fact if myString is immutable. As such, immutable objects can help enforce good object-oriented programming practices.

  • It's easier to make an immutable object thread-safe

  • It potentially simplifies the implementation of the class (nice if you're the one writing the class)

State

If you were to take all of an object's instance variables and write down their values on paper, that would be the state of that object at that given moment. The state of the program is the state of all its objects at a given moment. State changes rapidly over time; a program needs to change state in order to continue running.

Immutable objects, however, have fixed state over time. Once created, the state of an immutable object doesn't change although the state of the program as a whole might. This makes it easier to keep track of what is happening (and see other benefits above).

PCheese
  • 3,231
  • 28
  • 18
  • 1
    I would vote this up if you didn't act as if it only applied to OOP. – Rayne Mar 08 '09 at 00:23
  • @Rayne - Could you give an example of immutability in something other than OOP? The const keyword can work in a similar fashion; however I think of const as compiler-enforced and immutability as contract-enforced (that is, it's up to developer to make sure it's implemented correctly). – PCheese Mar 08 '09 at 01:06
  • @PCheese View my post below, I made an example in a functional programming language called Clojure. – Rayne Mar 08 '09 at 02:15
  • One issue that I think is often neglected is that it is possible for a class-type storage location to encapsulate its target's identity, mutable aspects of its target's state, both, or neither. A `List` will itself encapsulate the identities of the *objects* contained therein, but will not encapsulate their state. If one uses a `List` for the purpose of encapsulating the mutable states of the objects therein and one wishes to avoid having those states change unexpectedly, one must limit exposure of that `List` to anything that might try to mutate the objects therein. – supercat Sep 29 '12 at 17:36
  • "the current time...changes often". Subtly put. That one made me smile. – jinglesthula Mar 21 '18 at 19:35
  • None of the vague advantages of immutability seem to explain why it is the "default" for some data types. In your second bullet point, why doesn't Cocoa instead have classes "NSSstring" and "NSImmutableString"? Is the immutable version really so much more commonplace? – Morgan Rogers Nov 26 '20 at 14:01
23

Immutability

Simply put, memory is immutable when it is not modified after being initialised.

Programs written in imperative languages such as C, Java and C# may manipulate in-memory data at will. An area of physical memory, once set aside, may be modified in whole or part by a thread of execution at any time during the program's execution. In fact, imperative languages encourage this way of programming.

Writing programs in this way has been incredibly successful for single-threaded applications. However as modern application development moves towards multiple concurrent threads of operation within a single process, a world of potential problems and complexity is introduced.

When there is only one thread of execution, you can imagine that this single thread 'owns' all of the data in memory, and so therefore can manipulate it at will. However, there is no implicit concept of ownership when multiple executing threads are involved.

Instead, this burden falls upon the programmer who must go to great pains to ensure that in-memory structures are in a consistent state for all readers. Locking constructs must be used in careful measure to prohibit one thread from seeing data while it is being updated by another thread. Without this coordination, a thread would inevitably consume data that was only halfway through being updated. The outcome from such a situation is unpredictable and often catastrophic. Furthermore, making locking work correctly in code is notoriously difficult and when done badly can cripple performance or, in the worst case, case deadlocks that halt execution irrecoverably.

Using immutable data structures alleviates the need to introduce complex locking into code. When a section of memory is guaranteed not to change during the lifetime of a program then multiple readers may access the memory simultaneously. It is not possible for them to observe that particular data in an inconsistent state.

Many functional programming languages, such as Lisp, Haskell, Erlang, F# and Clojure, encourage immutable data structures by their very nature. It is for this reason that they are enjoying a resurgence of interest as we move towards increasingly complex multi-threaded application development and many-computer computer architectures.

State

The state of an application can simply be thought of as the contents of all the memory and CPU registers at a given point in time.

Logically, a program's state can be divided into two:

  1. The state of the heap
  2. The state of the stack of each executing thread

In managed environments such as C# and Java, one thread cannot access the memory of another. Therefore, each thread 'owns' the state of its stack. The stack can be thought of as holding local variables and parameters of value type (struct), and the references to objects. These values are isolated from outside threads.

However, data on the heap is shareable amongst all threads, hence care must be taken to control concurrent access. All reference-type (class) object instances are stored on the heap.

In OOP, the state of an instance of a class is determined by its fields. These fields are stored on the heap and so are accessible from all threads. If a class defines methods that allow fields to be modified after the constructor completes, then the class is mutable (not immutable). If the fields cannot be changed in any way, then the type is immutable. It is important to note that a class with all C# readonly/Java final fields is not necessarily immutable. These constructs ensure the reference cannot change, but not the referenced object. For example, a field may have an unchangeable reference to a list of objects, but the actual content of the list may be modified at any time.

By defining a type as being truly immutable, its state can be considered frozen and therefore the type is safe for access by multiple threads.

In practice, it can be inconvenient to define all of your types as immutable. To modify the a value on an immutable type can involve a fair bit of memory copying. Some languages make this process easier than others, but either way the CPU will end up doing some extra work. Many factors contribute to determine whether the time spent copying memory outweighs the impact of locking contentions.

A lot of research has gone into the development of immutable data structures such as lists and trees. When using such structures, say a list, the 'add' operation will return a reference to a new list with the new item added. References to the previous list do not see any change and still have a consistent view of the data.

Drew Noakes
  • 300,895
  • 165
  • 679
  • 742
9

To put it simple: Once you create an immutable object, there's no way to change the contents of that object. Examples of .Net immutable objects are String and Uri.

When you modify a string, you simply get a new string. The original string will not change. An Uri has only readonly properties and no methods available to change the content of the Uri.

Cases that immutable objects are important are various and in most of the cases have to do with security. The Uri is a good example here. (e.g. You don't want a Uri to be changed by some untrusted code.) This means you can pass a reference to a immutable object around without having to worry the contents will ever change.

Hope this helps.

Jeroen Landheer
  • 9,160
  • 2
  • 36
  • 43
7

Things that are immutable never change. Mutable things can change. Mutable things mutate. Immutable things appear to change but actually create a new mutable thing.

For example here is a map in Clojure

(def imap {1 "1" 2 "2"})
(conj imap [3 "3"])
(println imap)

The first line creates a new immutable Clojure map. The second line conjoins 3 and "3" to the map. This may appear as if it is modifying the old map but in reality it is returning a new map with 3 "3" added. This is a prime example of immutability. If this had been a mutable map it would have simply added 3 "3" directly to the same old map. The third line prints the map

{3 "3", 1 "1", 2 "2"}

Immutability helps keep code clean and safe. This and other reasons is why functional programming languages tend to lean towards immutability and less statefulness.

Rayne
  • 31,473
  • 17
  • 86
  • 101
  • If conj is creating a new map, why doesn't this map has a new name? Put another way: how do I access imap's old value? If I can't, then clearnly conj _is_ modifying the map –  Mar 08 '09 at 09:17
  • If you don't use imaps old value Clojure would garbage collect it of course... You can keep imaps old value by simply using it again, for example assigning it to a var. – Rayne Mar 08 '09 at 20:06
  • I don't really get that but then, I don't know Clojure so.. :) –  Mar 09 '09 at 19:12
  • @iraimbilanja, The way I explained immutability with Clojure is the same concept as with OOP but is explained in a way that seems bizarre. Immutability in functional programming language really shines. – Rayne Mar 10 '09 at 00:59
  • Wait... got it... so the value of imap is immutable but imap itself is mutable, i.e. it can be made to refer to a different object. so it's not a purely functional thing, I see. –  Mar 12 '09 at 19:16
  • def creates a new Clojure var. That var can be changed to refer to a different value. Clojure isn't purely functional, but it favors an immutable approach. There are refs and atoms to have actual mutable state. – Rayne Mar 13 '09 at 03:53
3

Good question.

Multi-threading. If all types are immutable then race conditions don't exist and you're safe to throw as many threads at the code as you wish.

Obviously you can't accomplish that much without mutability save complex calculations so you usually need some mutability to create functional business software. However it is worth recognising where immutability should lie such as anything transactional.

Look up functional programming and the concept of purity for more information on the philosophy. The more you store on the call stack (the params you're passing to methods) as opposed to making them available via references such as collections or statically available objects the more pure your program is and the less prone to race conditions you will be. With more multi-cores these days this topic is more important.

Also immutability reduces the amount of possibilities in the program which reduces potential complexity and potential for bugs.

Quibblesome
  • 25,225
  • 10
  • 61
  • 100
2

Why Immutability?

  1. They are less prone to error and are more secure.

  2. Immutable classes are easier to design, implement, and use than mutable classes.

  3. Immutable objects are thread-safe so there is no synchronization issues.

  4. Immutable objects are good Map keys and Set elements, since these typically do not change once created.

  5. Immutability makes it easier to write, use and reason about the code (class invariant is established once and then unchanged).

  6. Immutability makes it easier to parallelize program as there are no conflicts among objects.

  7. The internal state of program will be consistent even if you have exceptions.

  8. References to immutable objects can be cached as they are not going to change.(i.e in Hashing it provide fast operations).

See my blog for a more detailed answer.

Cadoiz
  • 1,446
  • 21
  • 31
Rajesh Dixit
  • 371
  • 3
  • 4
2

An immutable object is something which you can safely assume isn't going to change; it has the important property that everyone using it can assume they're seeing the same value.

Immutability usually also means you can think of the object as being a "value", and that there's no effective difference between identical copies of the object and the object itself.

MandyK
  • 1,201
  • 10
  • 11
2

Let me add one more thing. Besides all that was mentioned above you also want immutability for:

Community
  • 1
  • 1
David Pokluda
  • 10,693
  • 5
  • 28
  • 26
  • +1 for value objects. Immutability may or may not make sense in a lot of other cases depending on how you're using your objects, but I think it almost always makes sense to make value objects immutable. – AwesomeTown Mar 08 '09 at 00:00
  • How does this answer the question? – Rayne Mar 08 '09 at 00:24
  • Sorry for the necromancy - but immutability at a memory level is good for entites as well. If you want to change a value, you change it at the persistence layer, then reload it. One of the main benefits of immutability is it makes you structure programs that don't depend on internal states of objects in order to work correctly. – Mathieson Dec 18 '13 at 23:34
1

Immutability is about values, and values are about facts. Something has value if is unchangeable, because if something can be changed, then it means that no specific value can be connected to it. Object was initialized with state A and during program execution was mutated to state B and state C. It means that object doesn't represent single specific value but is only a container, abstraction on a place in the memory, nothing more. You can't have trust to such container, you can not believe that this container has the value you suppose should have.

Let's go to example - lets imagine that in code is created instance of Book class.

Book bookPotter =  new Book();
bookPotter.setAuthor('J.K Rowling');
bookPotter.setTitle('Harry Potter');

This instance has some fields set like author and title. Everything is ok, but in some part of the code again setters are used.

Book bookLor =  bookPotter; // only reference pass
bookLor.setAuthor('J.R.R Tolkien');
bookLor.setTitle('Lords of The Rings');

Don't be cheated by different variable name, really it is the same instance. The code is using setters on the same instance again. It means that bookPotter was never really the Harry Potter book, bookPotter is only pointer to the place where unknown book is located. That said, it looks like it is more a shelf then the book. What trust you can have to such object? It is Harry Potter book or LoR book or neither?

Mutable instance of a class is only a pointer to an unknown state with the class characteristics.

How then avoid mutation? It is quite easy in rules:

  • construct object with wanted state via constructor or builder
  • don't create setters for encapsulated state of the object
  • don't change any encapsulated state of the object in any of its methods

These few rules will allow to have more predictable and more reliable objects. Back to our example and book following above rules:

Book bookPotter =  new Book('J.K Rowling', 'Harry Potter');
Book bookLor = new Book('J.R.R Tolkien', 'Lord of The Rings');

Everything is set during constructing phase, in this case constructor, but for bigger structures it can be a builder. No setters exist in objects, book can not mutate to different one. In such case bookPotter represents value of Harry Potter book and you can be sure that this is unchangeable fact.

If you are interested in wider scope of immutability, in this medium article is more about the subject in relation to JavaScript - https://medium.com/@macsikora/the-state-of-immutability-169d2cd11310.

Maciej Sikora
  • 19,374
  • 4
  • 49
  • 50
1

Making things immutable prevents a large number of common mistakes.

For example, a Student should never have their student # change on them. If you don't provide a way to set the variable (and make it const, or final, or whatever your language supports) then you can enforce that at compile time.

If things are mutable and you don't want them to change when you pass them around you need to make a defencive copy that you pass. Then if the method/function you call changes the copy of the item the original is untouched.

Making things immutable means you do not have to remember (or take the time/memory) to make defencive copies.

If you really work at it, and think about each variable you make, you will find that the vast majority (I typically have 90-95%) of your variables never change once they are given a value. Doing this makes programs easier to follow and reduces the number of bugs.

To answer your question on state, state is the values that variables of an "object" (be that a class or a struct) have. If you took a person "object" state would be things like eye colour, hair colour, hair length, etc... some of those (say eye colour) do not change while others, such as hair length do change.

TofuBeer
  • 60,850
  • 18
  • 118
  • 163
1

"... why should I worry about it?"

A practical example is repetitive concatenation of strings. In .NET for example:

string SlowStringAppend(string [] files)
{
    // Declare an string
    string result="";

    for (int i=0;i<files.length;i++)
    {
        // result is a completely new string equal to itself plus the content of the new
        // file
        result = result + File.ReadAllText(files[i]);
    }

    return result;
}    

string EfficientStringAppend(string [] files)
{
    // Stringbuilder manages a internal data buffer that will only be expanded when absolutely necessary
    StringBuilder result=new SringBuilder();

    for (int i=0;i<files.length;i++)
    {
        // The pre-allocated buffer (result) is appended to with the new string 
        // and only expands when necessary.  It doubles in size each expansion
        // so need for allocations become less common as it grows in size. 
        result.Append(File.ReadAllText(files[i]));
    }

    return result.ToString();
}

Unfortunately using the first (slow) function approach is still commonly used. An understanding of immutability makes it very clear why using StringBuilder is so important.

Ash
  • 60,973
  • 31
  • 151
  • 169
1

You cannot change an immutable object, therefore you must replace it.... "to change it". i.e. replace then discard. "Replacing" in this sense means changing the pointer from one memory location (of the old value) to another (for the new value).

Note that in doing so we now using additional memory. Some for the old value, some for the new value. Also note, some people get confused because they look at code, such as:

string mystring = "inital value";
mystring = "new value";
System.Console.WriteLine(mystring); // Outputs "new value";

and think to themselves, "but I am changing it, look right there, in black and white! mystring outputs 'new value'...... I thought you said i couldn't change it?!!"

But actually under the hood, whats happening is this allocation of new memory i.e. mystring is now pointing at a different memory address and space. "Immutable" in this sense, is not referring to the value of mystring but rather the memory used by the variable mystring to store its value.

In certain languages the memory storing the old value must be manually cleaned up i.e. the programmer must explicitly release it..... and remember to do so. In other languages this is an automatic feature of the language i.e. garbage collection in .Net.

One of the places this really blows out re:memory usage is in highly iterative loops, specially with strings as in Ashs' post. Say you were building an HTML page in an iterative loop, where you constantly appended the next HTML block to the last and, just for kicks, you were doing this on a high volume server. This constant allocation of "new value memory" can quickly get expensive, and ultimately fatal, if the "old value memory", is not properly cleaned up.

Another problem is that some people assume things like garbage collection (GC) happens immediately. But it doesn't. There are various optimizations that occur such that garbage collection is set to occur during the more idle periods. So there can be a significant delay between when the memory is marked as discarded and when it is actually freed by the garbage collector.... so you can suffer large memory usage spikes if you simply defer the problem to the GC.

If the GC doesn't get a chance to operate before you run out of memory, then things wont necessarily fall over like in other languages that don't have automatic garbage collection. Instead the GC will kick in as the highest priority process to free up the discarded memory, no matter how bad the timing, and become a blocking process while it cleans things up. Obviously, this isn't cool.

So basically, you need to code with these things in mind and look into the documentation for the languages you are using for the best practices/patterns that allow you to avoid/mitigate this risk.

As in Ashs' post, in .Net and with strings, the recommended practice is to use the mutable StringBuilder class, rather than the immutable string classes when it comes to the need to constantly change a strings value.

Other languages/types will similarly have their own workarounds.

rism
  • 11,932
  • 16
  • 76
  • 116
0

Look, I haven't read the links that you have posted.

However, here is my understanding.
Every program holds some knowledge of it's data (State), which can change either by user-input/external changes etc.

Variables (values which change) are kept to maintain state. Immutable means some data which doesn't change. You can say, it is same as readonly or constant in some way (it can be seen it that way).

AFAIK, functional programming has things immutable (i.e. you cannot use assignment to a variable holding the value. What you can do is create another variable which can hold the original value + changes).

.net has string class which is an example.
i.e. You can't modify string in its place

string s = "hello"; I can write s.Replace("el", "a"); But this won't modify the contents of variable s.

What I can do is s = s.Replace("el","a");
This will create a new variable & assign its value to s (overwriting content of s).

Experts can correct mistakes if I have, in my understanding.

EDIT: Immutable = Unassignable once it is holding some value & can't be replaced in place (maybe?)

shahkalpesh
  • 33,172
  • 3
  • 63
  • 88
0

An example of the potential performance benefits offered by immutable objects is available in the WPF API. A common base class of many WPF types is Freezable.

Several WPF examples suggest that freezing objects (making them immutable at runtime) can improve application performance significantly as locking and copying is not required.

Personally I wish the concept of immutability was easier to express in the language I use most often, C#. There is a readonly modifier available for fields. I would like to see a readonly modifier on types as well that would only be allowed for types that only have readonly fields that are of readonly types. Essentially this means that all state would need to be injected at construction time, and that and entire object graph would be frozen. I imagine that were this metadata intrinsic to the CLR then it could be easily used to optimise garbage analysis for GC.

Drew Noakes
  • 300,895
  • 165
  • 679
  • 742
0

Sorry, why does immutability prevent race conditions (in this example, write after read hazards)?

shared v = Integer(3)
v = Integer(v.value() + 1) # in parallel
gatoatigrado
  • 16,580
  • 18
  • 81
  • 143
  • 1
    For types which are no larger than a `Long` or class reference, one can in general eliminate many race conditions when updating any single variable via the pattern `{int prev_v; do {prev_v = v;} while (Threading.Interlocked.CompareExchange(ref v, prev_v+1, prev_v);` Although the framework provides an Increment method for the particular case of adding one, one may perform any desired type of modification using that pattern. Essentially, it guarantees that all updates will be performed atomically in some sequence. – supercat Sep 29 '12 at 17:32