Navigate / search

One Less Thing To Worry About

In the closing scene of the movie Forrest Gump, we learn that Lt. Dan helped Forrest invest in some kind of “Fruit” company (Apple Corp.).

“So then I got a call from him, saying we don’t have to worry about money no more. And I said, that’s good! One less thing.”

That quote comes to mind whenever I think about immutability.

Let’s make a list of Int’s and display it.

    var numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    Console.WriteLine(string.Concat(numbers));

    Result: 123456789

Now, let’s reverse the list. In CSharp lists are mutable so we can do the following:

	var numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
	numbers.Reverse();
    Console.WriteLine(string.Concat(numbers));

    Result: 987654321

If we just wanted to show the numbers in reverse order, but keep the underlying list in order, this wouldn’t work. The Reverse method screws with our underlying data. The following would be nice.

	var rev = numbers.Reverse();

It’d be great if rev now pointed to a reversed list while numbers was still in order. That would be how an Immutable List would work, the Reverse method wouldn’t change anything, it would create a new List.

The result of the List object does the same, if you Sort, you sort the underlying data, you don’t get a new list. Add or Remove items, you change the underlying list.

There is a solution. There is an ImmutableList, just find Microsoft.Bcl.Immutable in Nuget and use System.Collections.Immutable, and you’re all set.

Except that’s a little problematic too. You lose that nice initialisation syntax that the old mutable list had.

	var numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

Under the hood CSharp relies on mutability to make that magic work.

So, you need to use NuGet and you need to write extra code, and all to stop you accidentally mutating a list. This is never going to be the default choice for CSharp developers.

IEnumerable
There is another choice, if we call the Reverse method as follows, we get the behavior we want.

	 rev = numbers.AsEnumerable().Reverse();

Problem solved. Well no. There’s an even bigger problem. Thanks to Jonas Elfstr√∂m for writing up this post on IEnumerable.

Take a look at this code and see if you can figure out where the result comes from.

    var numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    var rev = numbers.AsEnumerable().Reverse();
    numbers.Reverse();
    Console.WriteLine(string.Concat(rev));
    Console.ReadKey();

    Result: 123456789

As Enumerable isn’t really giving you immutable behavior. The variable ‘rev’ is still pointing to the same underlying data, just through the lens of a lazy operation. The AsEnumerable().Reverse() won’t happen until we use the results. Which means reversing the underlying numbers will cause them to be reversed back again when we eventually use the IEnumerable view of it.

This is why I would prefer data structures that are immutable by default. You avoid this kind of silliness. I’m sure many will have no problem whatsoever with the example above, but I find it annoying, unintuitive and confusing.

In CSharp I never get to put mutability aside as one less thing to worry about. You have to work really hard to make anything immutable, really immutable.

There is a place for mutable data structures and a place for immutable data structures. It seems logical to me that if one has to be harder work than they other, immutable should be the easier of the two. It should be the default.

Comments

Matt Edmondson
Reply

I agree and believe this is why languages such as F# are gaining ground. Immutability takes away another variable in our world of complexity mandating you to explicitly create a copy for modifications – exactly the kinds of issues passing by reference can cause.

Great post. I became aware of your blog from your talk at DDD North which resonated a lot with me. Many thanks.

Leave a comment

name*

email* (not published)

website