Introduction
Computer hardware has evolved dramatically in the last few decades. On a typical computer, storage and memory capacity have both increased a millionfold compared to the early 1980s. Nonetheless, standard industry practices in software development and mainstream ways of programming are not that different. Programming languages such as C++, Java, Python, and Ruby still typically encourage you to change things in place, and to use variables and mutate the state of a program, that is, to do things as if we were programming on a computer with a minimal amount of memory. However, in our quest for efficiency, better languages, and better tools, we reach for higher-level languages. We want to get further away from machine code. We want to write less code and let the computers do the tedious work.
We don't want to think about the computer's memory anymore, such as where a piece of information is stored and whether it's safe and shareable, as much as we don't want to know about the order of the instructions in the CPU. It is a distraction to the problems we are trying to solve, which are already complicated enough. If you have ever tried to do some multithreading in the languages cited previously, you will know the pain of sharing data between threads. Although, leveraging multicore CPUs with multithreaded applications is an essential part of optimizing a modern program's performance.
In Clojure, we work almost exclusively with immutable data types. They are safe to share, easy to fabricate, and improve the readability of our source code. Clojure provides the necessary tools to write programs with the functional programming paradigm: first-class citizen functions, which we will discover in the next chapter, and avoiding mutating and sharing the state of an application with immutable data types.
Let's dust off the dictionary and look up the definition of immutable, "Immutable: that cannot be changed; that will never change." It doesn't mean that a piece of information cannot change over time, but we record those modifications as a series of new values. "Updating" an immutable data structure provides a new value derived from the original value. However, the original value remains unchanged – those data structures that preserve previous versions of themselves are called persistent data structures.
Intuitively, we may think that such a persistent data structure would negatively impact performance, but it's not as bad as it seems. They are optimized for performance, and techniques such as structural sharing bring the time complexity of all operations close to classic, mutable implementations.
In other terms, unless you are programming an application that requires extraordinarily high performance, such as a video game, the benefits of using immutable data structures far outweigh the small loss in performance.