views:

310

answers:

5

Why are ints and doubles immutable? What is the purpose of returning a new object each time you want to change the value?

The reason I ask is because I'm making a class: BoundedInt, which has a value and an upper and lower bound. So I was wondering: should I make this type immutable too? (Or should it be a struct?)

+3  A: 

It makes sense to have BoundedInt as a mutable type because it represents a variable that at any point in time has a specific value and that value can be changed but only within a certain range.

However integers themselves aren't variables so they should not be mutable.

Mark Byers
+17  A: 

Firstly:

What is the purpose of returning a new object each time you want to change the value?

I think you might be mistaken about how value types work. This isn't some costly operation like you may be imagining; it's simply the overwriting of data (as opposed to, e.g., dynamic allocation of new memory).

Secondly: here's a very simple example of why numbers are immutable:

5.Increase(1);
Console.WriteLine(5); // What should happen here?

Granted, that is a contrived example. So let's consider a couple more involved ideas.

Mutable reference type

First, there's this one: what if Integer were a mutable reference type?

class Integer
{
    public int Value;
}

Then we could have code like this:

class Something
{
    public Integer Integer { get; set; }
}

And:

Integer x = new Integer { Value = 10 };

Something t1 = new Something();
t1.Integer = x;

Something t2 = new Something();
t2.Integer = t1.Integer;

t1.Integer.Value += 1;

Console.WriteLine(t2.Integer.Value); // Would output 11

This seems to defy intuition: that the line t2.Integer = t1.Integer would simply copy a value (actually, it does; but that "value" is in fact a reference) and thus that t2.Integer would remain independent of t1.Integer.

Mutable value type

This could be approached another way, of course, keeping Integer as a value type but maintaining its mutability:

struct Integer
{
    public int Value;

    // just for kicks
    public static implicit operator Integer(int value)
    {
        return new Integer { Value = value };
    }
}

But now let's say we do this:

Integer x = 10;

Something t = new Something();
t.Integer = x;

t.Integer.Value += 1; // This actually won't compile; but if it did,
                      // it would be modifying a copy of t.Integer, leaving
                      // the actual value at t.Integer unchanged.

Console.WriteLine(t.Integer.Value); // would still output 10

Basically, immutability of values is something that is highly intuitive. The opposite is highly unintuitive.

I guess that is subjective, though, in all fairness ;)

Dan Tao
Nice improvements in your last edit. If I could upvote a second time (in what is called Chicago style), I would.
Steven Sudit
It occurs to me that Java has an `int` primitive that is not an object as well as an `Integer` primitive wrapper class. This means that your contrived examples aren't really all that contrived. :-)
Steven Sudit
"Chicago Style" - love it
Joe
To vote Chicago style is to do it early and often.
Steven Sudit
+2  A: 

Anything with value semantics should be immutable in C#.

Mutable classes can't have value semantics because you can't override the assignment operator.

MyClass o1=new MyClass();
MyClass o2=o1;
o1.Mutate();
//o2 got mutated too
//=> no value but reference semantics

Mutable structs are ugly because you can easily call a mutating method on a temporary variable. In particular properties return temporary variables.

MyStruct S1;
MyStruct S2{get;set;}

S1.Mutate(); //Changes S1
S2.Mutate();//Doesn't change S2

That's why I don't like that most Vector libraries use mutating methods like Normalize in their Vector struct.

CodeInChaos
+3  A: 

As a mutable object, you have to lock an int variable before you change it (in any multi-threaded code that writes to your int from separate threads).

Why? Let's say you were incrementing an int, like this:

myInt++

Under the hood, this is a 32-bit number. Theoretically, on a 32 bit computer you could add 1 to it, and this operation might be atomic; that is, it would be accomplished in one step, because it would be accomplished in a CPU register. Unfortunately, it's not; there is more going on than this.

What if another thread mutated this number while it was in the middle of being incremented? Your number would get corrupted.

However, if you make a thread-safe copy of your object before you increment it, operate on your thread-safe copy, and return a new object when your increment is complete, you guarantee that your increment is thread safe; it cannot be affected by any operations on the original object that take place on other threads, because you're no longer working with the original object. In effect, you have made your object immutable.

This is the basic principle behind functional programming; by making objects immutable, and returning new objects from functions, you get thread safety for free.

Robert Harvey
I'm not sure this follows. In fact, the code you show is *not* thread-safe. You would need to use `Interlocked.Increment` to assure that.
Steven Sudit
I should also mention that it's impossible to lock an int, since it's not a reference type.
Steven Sudit
Thanks for changing your answer, but I'm sorry to have to say that it's still not correct. The `int` variable is mutable, as shown by the fact that you can increment it. And the increment operation, as mentioned, is not atomic. Even though it would normally be implemented as an `INC` opcode, that's not atomic with a `LOCK` prefix. It may be a single opcode, but translates to a fetch and a store, which means another core could win a race by storing in between those two steps.
Steven Sudit
@Steven: I never actually say in my answer that `int` is immutable, nor do I say that the increment is atomic, but only that it might be atomic if it were done in one step. The increment is probably not the best example, since it is multi-step anyway (it returns a value, then increments). My point is that you can return a new object and get thread safety, provided you can guarantee that the original object won't mutate during your operation. You can make that guarantee by creating a thread-safe copy of your original object, prior to performing your operation.
Robert Harvey
I realize that you're using numerous contrafactuals as part of your explanation, but they don't seem to be clearly identified as false.
Steven Sudit
@Steven: I don't know what that means. The answer says what it says, no more and no less. I think you're trying to read more into it than is actually there.
Robert Harvey
A contrafactual is a conditional known to be false. "If pigs had wings, then bacon would be kosher" is one example. In your answer, you say things like, "If int's were mutable, then we would have to lock them before changing them." The problem is that this isn't correct: int variables *are* mutable, whereas int constants are (like all constants) immutable. Also, we do have to use a lock (or the equivalent) to make changes atomic. Does that help explain my concern?
Steven Sudit
@Steven: Is that better?
Robert Harvey
Yes. I've converted my downvote to an upvote.
Steven Sudit
Robert, I just want to apologize if I came across as insufficiently clear or excessively picky. My motivation is to make your answer as good as it can be, not to be critical for its own sake.
Steven Sudit
@Steven: No worries.
Robert Harvey
+3  A: 

Integer variables are mutable. However, integer literals are constants, hence immutable.

int i = 0;

// Mutation coming!
i += 3;

// The following line will not compile.
3 += 7;

It's possible to make an integer field immutable, using readonly. Likewise, an integer property could be get-only.

Steven Sudit
Thank you for the downvote. Now I would appreciate it if you offered an explanation so that I can learn from my mistake.
Steven Sudit
It wasn't me...
Robert Harvey
@Robert: Didn't say it was. :-)
Steven Sudit
It would be great if someone were to point out the error. Otherwise, I might spend the rest of my life imagining that there's no error here.
Steven Sudit
Given the silence and the upvotes, I suspect that the original downvote may have been in error. If that is not the case, I am open to correction.
Steven Sudit
The argument isn't "is 3 immutable"... it's "is an integer immutable". Huge difference. i += 3 is the same thing as i = i + 3... it's still immutable. "an immutable object is an object whose state cannot be modified after it is created." You aren't editing i IN PLACE. You are editing it and returning it back into a "new" i. Just like "hello " + "world!" means NOTHING without an s = ... on the left side.
WernerCD
@WernerCD: Thanks for your response. For something to be immutable, it would have to be initialized to some state that cannot ever change. This clearly applies to an immutable class such as `System.String`, as there is a distinction between the instance and its references. For a struct like `System.Windows.Point`, there is a distinction between replacing the entire thing and just changing a part, so preventing the latter is a type of immutability. However, a `System.Int32` is a value without parts, so there is no distinction possible between wholesale replacement and modification.
Steven Sudit
If you declare an `int` field in a class, it is fully mutable, in that it can be changed at will. Not only can you use replacement semantics, such as `_x = 7;`, you can also use modification semantics, like `_x += 7;`. On the other hand, if you make it `readonly`, then neither sort of change is possible. Contrast this with, say, a `System.Text.StringBuilder`, for which `Append` is possible even when the field is `readonly` but replacement is not.
Steven Sudit
When there is a distinction between an instance and its references or a whole and its parts, then we can speak of immutability. But the distinction breaks down for self-valued value types, such as the "primitive" numerics. There is no way, even in principle, to answer the question of whether we are modifying the integer or replacing it. Therefore, contrary to what you said, integers are not immutable in any meaningful sense, although they can be made immutable through `readonly` and `const`.
Steven Sudit