14
$\begingroup$

This means variables can't be overwritten, arrays can't be modified, and nothing can be modified "in-place" - a new variable has to be created. Or if you need to modify it with a loop, then you would need recursion or foldl/similar.

What are the advantages and disadvantages of complete immutability?

$\endgroup$
3
  • $\begingroup$ Close voters, what's your reasoning? This seems perfectly fine to me $\endgroup$ Commented Jun 29, 2023 at 2:03
  • $\begingroup$ Don't you mean immutable objects rather than immutable languages? $\endgroup$
    – dan04
    Commented Jul 7, 2023 at 18:30
  • 1
    $\begingroup$ @dan04 The question appears to be about languages in which all objects are immutable. $\endgroup$ Commented Jul 7, 2023 at 22:53

5 Answers 5

10
$\begingroup$

Pro: Protection Against Accidental Mutations

Sometimes you'll want to pass objects to other functions. And if a pass-by-reference system is used by your language, that object may end up having changes made to it (looking at you python and Java). Immutability ensures that if you do something like adding items to a list you pass to a function, the original list in the calling scope won't be changed.

$\endgroup$
8
  • $\begingroup$ Being able to selectively protect against mutations (const) is what is ideal in my opinion. $\endgroup$
    – CPlus
    Commented May 18, 2023 at 0:30
  • 1
    $\begingroup$ But what if you don't know you need to have a const? Immutability provides blanket protection for the edge cases programmers haven't even thought about. There's been times I've been stung by mutability in places I thought there wouldn't have been a problem. $\endgroup$
    – lyxal
    Commented May 18, 2023 at 0:32
  • 1
    $\begingroup$ That is the other extreme. Everything mutable. But on the other hand making all immutable is extreme too. That is why the optimal is simply having a const. $\endgroup$
    – CPlus
    Commented May 18, 2023 at 0:35
  • 2
    $\begingroup$ Some "immutable" langs like OCaml still have refs that are mutable. But I think it kind of contaminates the purity. $\endgroup$
    – naffetS
    Commented May 18, 2023 at 1:25
  • 1
    $\begingroup$ const and immutable are different: const is a promise by the function that it won't modify the object, whereas immutable is (also) a promise to the function that the object won't be modified. const is a constraint/guarantee for/about the function, immutable is (also) for the rest of the code. $\endgroup$
    – Pablo H
    Commented Aug 3, 2023 at 16:20
9
$\begingroup$

Programs that use mutability are easier to write, up to a certain level of complexity. You have an object that contains a value that's almost what you need, so you mutate the object and you move on.

But programmers spend a lot more time reading code than writing code. And reading code is harder than writing code. Reading code with mutable state is a lot harder than reading code without mutable state. If the language guarantees that no state is mutable, it's a huge cognitive win.

One thing that makes mutability especially painful is concurrency. When multiple threads can access the same memory, you risk a race condition each time the program reads or writes an object with mutable state. This makes even very simple reasoning about programs hard. Simple-looking code like

# x : integer
assert (x + x) mod 2 == 0

may be wrong if x is actually mutable by another thread.

This concern can be alleviated by making the language fully immutable. But outside of some special-purpose languages, programs have to deal with the mutability of the world around them — they have to handle input-ouput. So good languages make mutability explicit. For example, in Haskell, all mutability has to be handled through monad operations. In Rust, the type system keeps track of which objects are mutable and of which objects are potentially shared: the world is immutable by default. In Erlang, all local computation is immutable, except for interprocess communication and direct access to a single, compound mutable object (the process state).

$\endgroup$
7
$\begingroup$

Two big advantages are lazy evaluation and memoisation. If computations don't have side-effects, such as by mutating data structures, then you don't need to compute them at all when their results don't matter, and you don't need to recompute them multiple times when their results do matter.

$\endgroup$
5
$\begingroup$

Disadvantage: Inefficient

In order to modify a single element in an array with for example 1000 items, all 999 other items would need to be copied over into a brand new array. This would add considerable overhead and memory usage.

$\endgroup$
6
  • 5
    $\begingroup$ It might be possible to implement them efficiently under the hood, although it might be prone to bugs. However, if you want to retain the original array in a separate variable, you have to copy it anyway. $\endgroup$
    – naffetS
    Commented May 18, 2023 at 0:29
  • 3
    $\begingroup$ For vectors that may be an issue, but for the far-more-commonly used (at least in general FP languages) linked list, CoW effectively solves that. $\endgroup$
    – blueberry
    Commented May 18, 2023 at 0:32
  • 6
    $\begingroup$ This is kind of a straw man argument; in a language with only immutable data structures, you probably would not use an array and copy the whole thing on every write. There are tree-based structures for persistent "arrays" which offer O(log n) performance for both reads and writes; typically the base of the logarithm is something like 32, so that the tree is at most 4 levels deep before you run out of memory anyway. There is still a significant overhead compared to mutable arrays, but it's not "copy the whole array on every write". See en.wikipedia.org/wiki/Persistent_data_structure $\endgroup$
    – kaya3
    Commented May 18, 2023 at 4:20
  • 3
    $\begingroup$ If all array members are immutable, copying the other 999 members to a new array is not overhead because we can still refer to those immutable members in the new array. Obviously, this depends on the language implementation. Some languages, like Ballerina, support this. $\endgroup$ Commented May 18, 2023 at 4:44
  • $\begingroup$ Your argument for inefficiency is the case of "[modifying] a single element in an array," but an immutable array cannot be modified. This is a logical error. Even if you instead meant that emulating mutation is inefficient, then this is unrelated to immutability for even in C that is an issue: sharing references to an array and having one modify it requires you to copy it to not be affected by that random mutation anyway. Either an object can be immutable, or it has to be copied, regardless of the language. $\endgroup$
    – Longinus
    Commented Jun 4, 2023 at 23:04
5
$\begingroup$

Advantages:

  • Immutable objects can be shared, including partial sharing (like a sub string of string, or sub array of array). If the shared objects are large, depending on the task the performance may even improve.
  • Immutable objects can always be passed and returned by reference. A value returned by function can just change the ownership.
  • Immutable objects are multi-thread safe. Race condition happens when two threads modify the same object in an unsafe manner. It is not possible to modify an immutable object.

Given array of floats of arbitrary size, how would you compute the sum of them?

int sum(const std::vector<int> & array, int p, int s) {
  return p == array.size() ? s : sum(array, p + 1, s + array[p]);
}

sum({1,2,4,8}, 0, 0) // it works, 15!

Mmm, does not this look a little bit too sophisticated? If not for you, go ahead with this idea. You can try to code this way without any special compiler, just accepting the agreed conventions.

Challenges

  • This approach may create lots of short lived values. It is important to allocate and later discard them in a multi-thread friendly way. With many threads, a mutex on a memory manager may create performance bottleneck.
  • Security critical data structures (passwords, private keys and the like) should generally have they data "shredded" (overwritten, made inaccessible) as soon as no longer needed. An immutable object cannot be easily overwritten by the programmer, so the language must care to destroy it as soon as it goes out of scope. See here, for instance.
$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .