Mutable or Immutable in a Parallel World

Problem

In recent years, computer processors have almost stopped getting faster. To improve new devices, hardware manufacturers add more processors instead of faster ones. Computers have had multiple processor cores for many years and there are now several phones on the market with four processor cores.

This imposes systems architects, designers and programmers to recognize the fact that using more than one core is needed for applications to run efficiently. Most developers have worked on multi-threaded applications for many, many years. Some developers use threads only when absolutely needed. Some use it extensively.

This article is about business data. More exactly, should your business model use classes that are mutable or immutable? Immutable classes can never change their data, once they are created. Mutable classes can change even after the initialization and constructor has been run.

To be thread safe when throwing your business model objects around, immutable data is the obvious choice. It is an easy choice in a language like Erlang, since mutable data is not even allowed – and it works well.

  • No mutable data means no locks
  • No mutable data means easy to parallelize

So great, let’s use that! However, if you try to bring the immutable paradigm to e.g. C#, you will get some problems because many features in C# are not developed for totally immutable classes. One problem is different libraries for serialization for instance.

System.Threading.Tasks in C# has made things a lot easier to parallelize code. C# 5 is going to include async and await keywords, that will make parallel programming much more simple, with elegant solutions for async exception handling etc. All these improvements will increase the amount of code that is written for parallel execution.

Mobile devices have hard restrictions on running code on the main thread – why most mobile developers have to use concurrency in response to most things that the user does – at least a high performing app.

To summarize:

  • Hardware is not getting faster, but more processor cores
  • Touch based UIs feel bad when code runs more than 16 ms on the UI thread
  • Language and library support makes it easier to parallel
  • Business Model classes need to be thread safe or cause hard to debug issues

One Solution

When you encounter a choice between two things and it is very hard to decide what to go with, the answer is often, you need both.

I recently spent some time on making a solution where both Immutable and Mutable versions of business models exists. It was hard work but now it works like a charm. The important things were:

  • It should be easy to work with serialization.
    • …which means that support for an empty constructor is needed.
    • At least some part of each business model must be mutable.
  • The solution should be totally thread-safe for immutable versions. Now, this implies a few things:
    • Lists in the immutable version must not support mutable methods, like “Add”.
    • It must *not* be possible to cast a mutable version into an immutable version, nor the other way around – without creating a copy (which can be done with manually implemented cast operators).
  • The compiler should impose correct usage, not the runtime.
    • The immutable version must have collections that does not even have mutable methods – having mutable methods that throws exceptions is not enough.
  • You should not accidently be able to cast a mutable version into an immutable version or the other way around, without getting a copy
    • This rules out a mutable class with an immutable interface and some other options.
  • Validation code must only be written once.
  • You should have to write as little code as possible for each new model (this is the least important requirement, but still…).

After trying all kinds of interfaces with and without explicit implementation, inner classes using the builder pattern, frozen object (where you freeze the object after setting it up properly),  structs, normal inheritance and mixes between all these and other patterns… I finally came to the following solution:

  • One class for the immutable version and one class for the mutable version of each business model object.
  • Manual cast operators that can be used (but I eventually chose to remove them but they are in the code below – commented out).
  • One abstract base class for immutable models and one for mutable models.
This is how I wrote the abstract base classes for MutableModel and ImmutableModel:

    public abstract class ImmutableModel<TImmutableModel, TMutableModel>
        where TImmutableModel : ImmutableModel<TImmutableModel, TMutableModel>
        where TMutableModel : MutableModel<TImmutableModel, TMutableModel>
    {
        public abstract TMutableModel ToMutable();
/*
 * This cast operator is dangerous and should not be used. As soon as I removed it, I found bugs both here and in user code.
        public static implicit operator TMutableModel(ImmutableModel<TImmutableModel, TMutableModel> immutableModel)
        {
            Console.Out.WriteLine("OPERATOR ToMutable");
            return immutableModel.ToMutable();
        }
*/
        protected string ImmutableCopy(string s)
        {
            return s; // C# strings are immutable, hence thread-safe, so copy is unnecessary
        }
        protected ReadOnlyCollection<string> ImmutableCopy(IList<string> mutableList)
        {
            if (mutableList == null)
                return null;

            return new ReadOnlyCollection<string>(new List<string>(mutableList));
        }
        protected
            TOtherImmutableModel
            ImmutableCopy<TOtherImmutableModel, TOtherMutableModel>
            (TOtherMutableModel mutableModel)
            where TOtherImmutableModel : ImmutableModel<TOtherImmutableModel, TOtherMutableModel>
            where TOtherMutableModel : MutableModel<TOtherImmutableModel, TOtherMutableModel>
        {
            if (mutableModel == null)
                return null;

            return mutableModel.ToImmutable();
        }
        protected ReadOnlyCollection<TOtherImmutableModel>
            ImmutableCopy<TOtherImmutableModel, TOtherMutableModel>
            (IList<TOtherMutableModel> mutableList)
            where TOtherImmutableModel : ImmutableModel<TOtherImmutableModel, TOtherMutableModel>
            where TOtherMutableModel : MutableModel<TOtherImmutableModel, TOtherMutableModel>
        {
            if (mutableList == null)
                return null;

            var immutableList = new List<TOtherImmutableModel>(mutableList.Count);
            foreach (TOtherMutableModel mutableModel in mutableList)
                immutableList.Add(mutableModel.ToImmutable());
            return new ReadOnlyCollection<TOtherImmutableModel>(immutableList);
        }
    }

    public abstract class MutableModel<TImmutableModel, TMutableModel>
        where TImmutableModel : ImmutableModel<TImmutableModel, TMutableModel>
        where TMutableModel : MutableModel<TImmutableModel, TMutableModel>
    {
        public abstract TImmutableModel ToImmutable();
/*
 * This cast operator is dangerous and should not be used. As soon as I removed it, I found bugs both here and in user code.
        public static implicit operator TImmutableModel(MutableModel<TImmutableModel, TMutableModel> mutableModel)
        {
            Console.Out.WriteLine("OPERATOR ToImmutable");
            return mutableModel.ToImmutable();
        }
*/
        protected string MutableCopy(string s)
        {
            return s; // C# strings are immutable, hence thread-safe, so copy is unnecessary
        }
        protected IList<string> MutableCopy(ReadOnlyCollection<string> immutableList)
        {
            if (immutableList == null)
                return null;

            return new List<string>(immutableList);
        }
        protected
            TOtherMutableModel
            MutableCopy<TOtherImmutableModel, TOtherMutableModel>
            (TOtherImmutableModel immutableModel)
            where TOtherImmutableModel : ImmutableModel<TOtherImmutableModel, TOtherMutableModel>
            where TOtherMutableModel : MutableModel<TOtherImmutableModel, TOtherMutableModel>
        {
            if (immutableModel == null)
                return null;

            return immutableModel.ToMutable();
        }
        protected IList<TOtherMutableModel>
            MutableCopy<TOtherImmutableModel, TOtherMutableModel>
            (IList<TOtherImmutableModel> immutableList)
            where TOtherImmutableModel : ImmutableModel<TOtherImmutableModel, TOtherMutableModel>
            where TOtherMutableModel : MutableModel<TOtherImmutableModel, TOtherMutableModel>
        {
            if (immutableList == null)
                return null;

            var mutableList = new List<TOtherMutableModel>(immutableList.Count);
            foreach (TOtherImmutableModel immutableModel in immutableList)
                mutableList.Add(immutableModel.ToMutable());
            return mutableList;
        }
    }

This is an example of a business model that derives from the abstract base classes:


    public class User : ImmutableModel<User, MutableUser>
    {
        public User(MutableUser mutableUser)
        {
            MainPhoneNumber = base.ImmutableCopy(mutableUser.MainPhoneNumber);
            MyStringList = base.ImmutableCopy(mutableUser.MyStringList);
            MyDto = base.ImmutableCopy<User, MutableUser>(mutableUser.MyDto);
            MyDtoList = base.ImmutableCopy<User, MutableUser>(mutableUser.MyDtoList);
        }
        public override MutableUser ToMutable()
        {
            return new MutableUser(this);
        }
        public string MainPhoneNumber { get; private set; }
        public ReadOnlyCollection<string> MyStringList { get; private set; }
        public User MyDto { get; private set; }
        public ReadOnlyCollection<User> MyDtoList { get; private set; }
    }

    public class MutableUser : MutableModel<User, MutableUser>
    {
        public MutableUser() { }
        public MutableUser(User immutable)
        {
            MainPhoneNumber = base.MutableCopy(immutable.MainPhoneNumber);
            MyStringList = base.MutableCopy(immutable.MyStringList);
            MyDto = base.MutableCopy<User, MutableUser>(immutable.MyDto);
            MyDtoList = base.MutableCopy<User, MutableUser>(immutable.MyDtoList);
        }
        public override User ToImmutable()
        {
            return new User(this);
        }
        public string MainPhoneNumber { get; set; }
        public IList<string> MyStringList { get; set; }
        public MutableUser MyDto { get; set; }
        public IList<MutableUser> MyDtoList { get; set; }
    }

And this is an example of how the objects are used in the application when working with the mutable and immutable model classes:

            MutableUser mu = new MutableUser();
            mu.MainPhoneNumber = "+1-555-555";
            mu.MyStringList = new List();
            mu.MyStringList.Add("Test");

            // Now, let's create an immutable copy
            User u = mu.ToImmutable();

            // Objects in a cache should be immutable
            Cache(u);

            // It is safe to send the same immutable copy to an async method
            DoSomethingAsync(u);

            // Changing the MutableUser does not affect the immutable copy
            mu.MyStringList = null;

What We Achieved

So, the base classes are hard to read because of the generics that ties the two types together but you very rarely need to alter that code.

The Model Classes are straight forward to write:

  • They have to define their properties, obviously.
  • They have to implement ToMutable and ToImmutable. The compiler will tell you if you forget to implement them.
  • They have to implement a constructor that copies data from the mutable type to the immutable and the other way around. Base class methods exists that are simple to work with exists. The compiler will tell you if you are trying to make an immutable model with mutable models as properties.
  • They do need to be implemented twice (both mutable and immutable classes, instead of just one POCO), which is kind of boring but not really hard nor very time consuming because it is very easy.

Let’s see the upside, that is, all the rest of your code – that is using your model:

  • The immutable version of the model is completely immutable, including collections. Hence, those objects can be used alone, in object trees, in data caches,  to operate on data asynchronously etc.
  • The mutable version works like any other class and can be used in serialization operations, you can add [DataContract] and [DataMember] to it if you need to work with WCF etc. You might also want to use the mutable version when asking the UI to stuff the user’s entered data into a Business Model – since the UI is pretty much always single threaded.
  • We can easily convert between the mutable and immutable versions – back and fourth.
  • We can not accidently send a mutable copy off to an async method with an immutable facade.
  • Most of the work is in the abstract base classes and adding Model Classes it straight forward.

If it is very important for you to be able to cast between the mutable and immutable versions, without running ToMutable() and ToImmutable(), you can uncomment the manually implemented implicit cast operators. I actually had those when I developed this and some of the using code. At the time I uncommented them, I found a few bugs on all levels – so I do recommend that you stick with the ToMutable() and ToImmutable(). Observe though, that the way the cast operators are implemented, they are still perfectly thread safe – it just may not occur to the user of the model classes that new copies are made when casting.

Next Steps

If anyone can find a solution for moving the ToMutable and ToImmutable methods down to the base classes, that would be sweet. Please comment here if you find a way!

This implementation only supports strings, List<string>, xxxModels and List<xxxModel>. Next steps is obviously to add support for more types of data.

Would it be nice to move the constructors down to the base classes and implement them using Reflection instead? We could add attributes to the data fields, which would result in:

    public class User : ImmutableModel<User, MutableUser>
    {
        public override MutableUser ToMutable()
        {
            return new MutableUser(this);
        }
        [ModelData] public string MainPhoneNumber { get; private set; }
        [ModelData] public ReadOnlyCollection<string> MyStringList { get; private set; }
        [ModelData] public User MyDto { get; private set; }
        [ModelData] public ReadOnlyCollection<User> MyDtoList { get; private set; }
    }

    public class MutableUser : MutableModel<User, MutableUser>
    {
        public MutableUser() { }
        public override User ToImmutable()
        {
            return new User(this);
        }
        [ModelData] public string MainPhoneNumber { get; set; }
        [ModelData] public IList<string> MyStringList { get; set; }
        [ModelData] public MutableUser MyDto { get; set; }
        [ModelData] public IList<MutableUser> MyDtoList { get; set; }
    }

Now is that better or worse?

If you find anything wrong, something you like/dislike, approve of or disapprove of, please comment.

References

Liskov substitution principle
Immutability in C# (and following articles)
Task Parallel Library
Threading in C#
Understanding SynchronizationContext
Martin Fowler on UI Frameworks
Erlang Immutability Summary