What does "immutable" really mean?

Asked

Viewed 5,608 times

76

In that question I did about value types and reference types in C#, it was quoted in the accepted answer that instances of structs must be immutable objects. Reading on wikipedia confirmed that an immutable object should not have its modified state after being created. But this is confusing, because after all, if I declare a variable to be of a type struct or class, I can modify its value at any time, or else it would be a constant.

In fact, int is a struct as far as I know, so in this case if I declare a variable of type int it should not be modified if instances of structs are really immutable. But this is clearly not true, I can do int i = 1 and then i++ and in that alone have I changed its state.

In this way, what really means "immutable objects" and how this concept is a useful concept in object orientation?

3 answers

67


Variable X value

Values may be changeable or immutable, variables that contains values are always, by definition, mutable. Variable refers to something that varies, that changes, that is mutable.

It is common for those who write about the subject to use a looser language and not so academic, which can lead to a really wrong understanding. You are right in the doubt. It is rare who writes with all the details specified in the most correct way possible in these cases. And that’s what I did there in the question. That’s why this new question fits. So if someone says "variable x is immutable", read "the content of variable x is immutable".

Constants

Just as a constant, by definition, is immutable. I would like to say that the values of the constant are also always immutable. In C# they are, but some languages have managed to let a constant have the changeable value. It is a conceptual error that C# did not commit.

At least the C# makes it clear that PI and the MaxValue of the kind int are constant. Whereas MaxThreads and PercentualDeDesconto sane readonly, that is, they do not change during the entire execution of an application instance, but can change from one execution to another, or from one version to another. There are some other differences that are not the case now.

Struct X Class

It is highly recommended that structs have their immutable values. This is in the C#documentation. It is very strange to have an object by value that is mutable. Technically it’s possible because can there is some situation that this is really useful and correct.

Classes tend to be changeable. But they don’t have to be. The biggest example is string which is a type by reference but which has semantics of value, that is, its value is immutable.

But how? I can change the value of a string. I can do:

var s = "teste";
s = "mudei";

There was a change in the value of the variable and not the value of the string. You didn’t change the text, you created a new text and changed the reference (which is in the variable) to this new text.

Example of immutability

Look at this struct:

struct Point {
    public float X;
    public float Y;
}

It is changeable. It should not. Why?

You can change the value of X and Y independently. You are changing the value itself and not the variable. And a struct should not allow this.

It is not strange to change a part of the point without it being another point?

struct Point {
    private int x;
    private int y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

It already changes everything. The only way to change a variable value is to completely change the value contained in it. In this case you create a new value and allocate it to the variable, you do not change the value allocated to it. Its use would be:

var p = new Point(2, 5);
// p.x = 7; seria um erro, você nem consegue acessar os membros individualmente.
p = new Point(7, 5);

Realized you can’t change just one member of the value? You have to create a new value and associate this new value with the variable. This is immutability.

You change not only the value, you change his identity. Cartesian point 2, 5 is different from point 7, 5, not the same point with a different characteristic.

Note adding public properties to access members x and y do not cause problems. But if you allow assigning values to them, you fall into the same problem as the previous example of Point. Then you could create a property just with get.

Example of mutability

Now look at this code:

struct Pessoa {
    public string nome; 
    public int idade;
    public Decimal salario;
    public DateTime CadastradaEm;
}

public class Exemplo {
    static void Main() {
        Pessoa p;
        p.nome = "João";
        p.idade = 18;
        Console.WriteLine("{0} tem {1} anos", p.nome, p.idade);
       
        p.idade = 20;
        Console.WriteLine("{0} tem {1} anos", p.nome, p.idade);
    }
}

Another problem

Leaving aside that this is exposing fields for external direct access (it is just a simplified example, I do not need to follow such "good practices" here), what is the problem of this code?

If this is a struct should be immutable but this is not the error. As it stands, it is clearly a mutable object, you can change members individually. No problem. That’s probably what you want in this case.

If this structure has several fields, it gets very large. Large objects do not look good in a struct. After all structs are copied by value. Copies of large objects are inefficient. So we already have two reasons, inefficiency and mutability to say that this should be a class. The mistake was to define this structure as struct instead class. More details in this question.

Identity

In this example above despite changing the age of the person, the person is the same, the identity of the object is the same, so the object is mutable. Features of the object can be changed without the object becoming something else. Even if you changed the name from "John" to "Mary" (very common today :) ) it would still be the same person. A name can be exchanged for several factors, such as typo or change of marital status, just to name the most common.

When you use the code:

var i = 0;
i = 5;

Semantically is the same as saying:

var i = new Int32(0);
i = new Int32(5);

Int32 is the one struct which equals to int. Note that each value change, you are creating a new object, with equivalent identity. Actually it is being "stored" in the same place, it is replacing the previous value, but not only the variable has changed, but its value as a whole has changed.

So in the example:

int i = 0;
i++;

is the same as:

int i = default(Int32); //só para mostrar outra forma de interpretar a mesma coisa
i = new Int32(i + 1);

It doesn’t mean that the compiler interprets it exactly like this, but it’s a way that helps understand.

Note that the value of i is being changed as a whole, according to the definition I gave above.

In C# 7 it is possible to use readonly struct that favors the immutability.

In C# 9 it is easier to adopt it with the init on the estates.

Completion

It seems strange at first glance, but if you look at the variable itself and its value as distinct things it is easy to understand that the value never changes, it is replaced by another.

The easiest way to understand immutability is to look at whether the change can occur in parts of the object (fields, properties, elements of a array, etc.) or may occur only in its entirety.

Immutability need not fit into any strict definition of object orientation. It is a concept applied to any paradigm.

The subject is very vast and is something that few understand correctly. I think I don’t understand everything correctly. But here’s an important part about it.

More information (the question is about Java, but the idea is the same).

Exercise

Answer if a phone that has the DDD (area code) and the number itself, can exchange your DDD and remain with the same identity? And he can switch operators and keep his identity?

If you create a variable (of type) int with 0 and then assign 0 to it, it changed the identity?

Answers in the comment below

I put in the Github for future reference.

  • So the idea that an object of type struct represents a unique value that comes from this idea of immutability? That it makes no sense to alter attributes alone, but only to manipulate the object as a whole?

  • @user1620696 I think I answered in the issue. but basically, yes. Answer and then I say if you got the "exercises" I left :)

  • An important observation (and "pedantic" almost): the Point struct, as defined (the version with private Fields), is not strictly immutable, although it is "effectively immutable". This detail can have significant implications for multi-threading codes (ie, completely wrong code although apparently correct vs correct code). Otherwise, very good answer! Details: http://stackoverflow.com/questions/16678416/different-between-immutable-immutable-and-effectively-immutable-objects

  • @Brunoreis quite relevant for a more complete understanding yes. I wanted to simplify and focus on what was most important to the issue.

  • Answering the questions, it seems to me that changing the DDD really changes the identity of the phone. Already changing the carrier does not change the identity. And finally the int will change the identity yes, taking into account the way I understood that the compiler understands assignments to types value.

  • 3

    @user1620696 About the phone is just that. In real life it’s like this, in the model, it should also be. The case of the int It’s more complicated. Some say you change your identity, some say you don’t. In fact its logic is correct, by coincidence the value is the same, but the identity is different. But some people say that if you can’t have anything that differentiates the identity is the same. That makes sense too. So unless someone gives me a clear reference to that, I leave both as true. Even because in this case only has use theoretically. No phone has a practical change.

  • 1

    This idea of one value being exchanged for another instead of being changed is clearer now.

Show 2 more comments

26

The big question that’s confusing to you is about what is an object.

Object, physically speaking, it is a space allocated in memory to keep your reference and use it in your code you need a variable to reference it. So notice that there is a difference between the unchanged object and the unchanged variable.

A variable that does not change would be a constant, the moment you assign an object to it you can no longer change objects.

An unchanged object is an immutable object, you cannot change its attributes after initializing for the first time, however you can at any time create a new object and assign it to the same variable that referenced the old one.

When you do i++ you are creating a new incremented object from one of the previous object and assigning that new object to the same variable i.


Sorry I don’t go too deep, but I don’t know the syntax of C#, I know Java where the concept is the same.

  • This subject involves a much more subtle and "high-level" conceptual understanding of "objects" in "object-oriented programming" than a simplification such as "a space allocated in memory" and "bytes that change or do not change". See the bigown response above.

  • 2

    But I don’t think the answer deserves a negative. It is a simplification that helps those who are not looking for all the details. Apparently only OP and I like her :)

  • 4

    Immutability, identity and equality are topics that usually tie knots in the head of some of my students. I liked the two answers. I know this is a little far from what some people are looking for these days, but I owe a lot of what I’ve learned about O to Disassembly of Visual Studio, which showed/described all C code instructions++.

2

I wrote an article about this (https://epxx.co/artigos/imutavel.html). The basic idea is as follows: in some languages, there is no distinction between "value of" and "reference to" an object. For example, in Java an object variable always contains a reference. Idem Delphi, idem Python... Suppose that 5 variables contain the same object of type A:

a = new A("bla"); b = a; c = b; d = c; e = d;

If the object is changed via any variable, all 5 variables are affected because they all contain references to the same object.

For some classes, it makes more sense to behave like a primitive type, like integer or string. For example, if I do

a = 100; b = a; c = b; d = c; e = d;

I really hope that by making b = 200, the other variables remain at 100, don’t you think? Idem if it was a string; when we assign one string variable to another, what we really want is a copy of the original string, not two variables pointing to the same string.

Constructing the class so that it is immutable after construction allows its objects to simulate this behavior of primitive types. For example, in Java the String is an immutable class. When I do

a = "bla"; b = a; c = b;

It is true that at first the three variables refer to the same String object. However, since it is an immutable object, the only way to change the value of a variable is to assign it another completely new String. All String handling methods return new Strings, never touch the original.

Under the hood, classes with String use various tricks to improve their performance, but always maintaining the appearance of immutability. For example, if we do

a = "bla"; a += "ble";

can be that the String implementation checks how many references there are to the "bla" object. If there is only one, then the concatenation of "bla" and "ble" can modify the original String. If there are two or more references (b, c...), the "copy-on-write" is made, that is, a new "blable" string is created only for the variable "a".

Browser other questions tagged

You are not signed in. Login or sign up in order to post.