Immutability has been well-understood for some time. Python, Java, and C++ have different memory models that make direct comparisons difficult. The author of the article you originally cited doesn't seem to know C++.
As in Python, Java, and most multi-paradigm languages, C and C++ allow mutability by default. This is what programmers usually want: if I have an
String x variable, I want to be able to assign a new value
x = "foo".
The const-system in C and C++ allows a great deal of nuanced immutability that is lacking in Python, Java, and even Scala. If a C++ function takes a
const std::string& or a
const char*, it promises (and the compiler ensures, to some degree), that this function will not (cannot!) change the contents of that string. Given a const object, we can only invoke methods of that object that are also marked as const. If a C++ class only has public members which are
const, then the object is effectively immutable.
However, this is sometimes confusing as in C and C++ objects are memory locations, and variables are names for memory locations. In contrast, variables in Python and Java are names for pointers to objects. In a language with reference semantics,
x = y means “make x point to the same object as y”. Since we are only copying pointers, this is possible with immutable objects. In a language with value semantics like C++, it means “update the contents of
x with the contents of
y”. Therefore, if reassignment of a variable is desired in C or C++, the variable may not have a const type. To do this with immutable objects, we would have to use a pointer explicitly.
That Java and Python use immutable string objects is a fundamental design decision, but it is not directly connected to the benefits of immutability in a multithreading environment. One reason is that string literals in the source code can be pooled which reduces the number of objects. This is possible in C/C++ as well. In C++ the literal
"foo" has type
const char (the 4th char is the terminating
'\0'). Another reason is that entries in sets and keys in dicts/maps must not be changed. Since strings are used pervasively as dict keys (most Python objects are a dict), immutability removes a common source of errors. In Java, another reason for immutable strings is the Java security model. All of these reasons are entirely unrelated to multithreading.
If Java were built with immutability in mind, the language would have looked very differently. While it is closely inspired by C++, the designers tried hard to create a much simpler language, getting rid of const is one such step. The equivalent Java thing to a C++ const reference is an adapter or decorator that implements any mutating methods as
throws new NotImplementedException(), and forwards non-mutating method calls to the actual collection. The fact that the java.util collection interfaces all imply mutability is a clear sign they didn't strive for an immutability-first language.
The solution Java put forward to solve concurrency problems wasn't immutability, but pervasive locking. Every single object contains a mutex that may be used for
synchronized blocks or entire methods. As it turns out, that is not good for performance, does not scale very well, and is quite error prone – you still have to deal with mutable global state.