If you’re keeping up with the latest trends in programming, you’ve probably heard a lot about immutability.
It’s particularly relevant to front-end development right now, with React and Angular 2 both touting it as a feature. But immutability is nothing new. Languages like Haskell, Erlang, and various flavors of Lisp and ML have featured some aspects of immutability for a long time, so the benefits of it are fairly well understood. Most importantly, immutability can help prevent a large class of bugs.
What is Immutability?
An immutable value cannot be changed after its value has been assigned.
Boom, done, programming is solved, no more bugs.
Well, not quite. Immutability is just one tool, not a panacea, but knowing when to use it can provide significant benefits to your codebase. You’ll find it’s often possible to write the same program in either an immutable or mutable fashion. At Clara, we lean towards the former.
Time for a contrived example with an old favorite — Java!
As defined below, Dog is a mutable class:
How do I know? Because I can change its values after it’s been created:
See how I was able to change name and age even after I set them initially?
“So what?” you say, “I want my values to be easy to access and change.”
What if my class is just a little more complex:
I can invoke it like this:
But wait a second — who’s trained now? Is it Scout? Clementine? Whose friend is who?
Let’s take a look at what myDog looks like at the end of this:
Oops! Now, Scout’s tricks are duplicated and where did Clementine go?
Sure, this is a contrived example, but bugs like this pop up all the time.
The Way Forward
We need to do a few things to fix this problem:
- Use Java’s final keyword to prevent changes to name, age, friendNames, and tricks. Note that this will still allow you to make changes to the contents of friendNames and tricks, because final only makes the reference immutable.
- Have train return a new Dog instance.
- Use a library that provides immutable data structures, such as Guava.
Or! You could use a programming language that gives you all of this for free. At Clara, we’re fond of Scala.
Using Scala, we can make references immutable using val:
Even better, our Dog class can be made immutable by defining a case class:
With this definition, Scala won’t let us re-assign Dog’s properties:
When you start writing code using immutability, you’ll realize you can’t do a lot of things you took for granted before. Let’s take a look at another example:
This will give us error: value push is not a member of Seq[Entry]. Immutable data structures like Seq don’t have a push method!
Okay, so let’s try this:
We’re not trying to modify the Seq, we’re using our immutable value. But then… error: reassignment to val!
I can already hear you — ”Immutability sucks. It’s making everything difficult and I’m writing too much code and it’s not worth it!”
There are definitely trade-offs with immutability, and this is one of them. You’re going to have to start thinking about the flow of your programs in a different way.
In the above example, we’re going to have to go all-in on immutability to have it make sense:
With our new definition, AddressBook is totally immutable, and now consumers of AddressBook are going to have to use it in an immutable way, too.
Immutability gives us some tangible benefits:
- Correctness and Reasoning – When values are immutable, you know that they won’t be changed by any other part of your code. Using this, we can look at individual functions and know whether they act correctly. This makes it much easier to make changes to a codebase when you don’t have to worry whether some ominous unknown global may be affecting what you’re working on.
- Concurrency – When your values are immutable, a significant amount of problems in concurrent programming disappear. The need for semaphores goes out the window. Values can be given to other threads without you worrying as much about the state changing without your knowledge.
If you’ve used Java, you know that some classes are explicitly marked as thread safe (e.g. ConcurrentHashMap). When a class is immutable, it is thread safe by default. This may have performance implications, but if data integrity is the biggest issue for you (as it is for us at Clara), this trade-off is a no-brainer.
Immutability and You?
Using immutability in your programs isn’t a silver bullet, but it’s a pretty powerful one. I’ve personally really enjoyed working with languages and projects that offer immutability as a first-class citizen — they’re easier to reason about, easier to extend, and easier to debug.