Refactoring to maintainability

Into Adventure - Part 3 - State

The difference maker

In his 2013 presentation at NDC London, Robert C. Martin, talked about some different types of programming paradigms. Well, the three major ones: Procedural, Object Oriented and Functional. On it, he said that what makes Functional different from the rest is Immutability. I have since think about that, and I believe I agree with him. Everything else that I am talking on these posts, which tend to be consider part of a functional style of programming, is actually present on the other paradigms.

Another way to look at it is that Functional Programming is about controlling side effects. 100% purity is impossible. Programs will not be able to make something useful. Any kind of input and output is a side effect. But with FP you can limit and ring-fence those places where it happens. The more strict is this containment, the more pure is the language. On one side, with very few limitations you would have something like F#, where mutable state can easily mingle with immutable state. On the other side you have things like Haskell, where the use of Monads (I am not going to talk about them today) completely surrounds side effects and mark them.

A case of immutability

Let’s look at some code:

// Java
List<Integer> list = new ArrayList<Integer>();
list.add(5);
list.add(4);

The above code creates an empty list of Integer objects and then adds two values to the list. If we were to print the list contents we will get “4” and “5” out.

Let’s going to try to do the same on a FP language:

;; Clojure
(defn list-changer []
  (let [first []
       second (conj first 4 5)]
       (println first)
       (println second)))

The first [] command creates an empty array binded to the key first. The line second (conj first 4 5) creates a new array which is the combination of the one that is binded to first and the values 4 and 5 (there are some optimizations done by Clojure, if you want to investigate them). That means that the array that is identified by first remains unchanged and we have a new array identified by second. The lines that print them will show exactly that.

We can try something slightly different:

// Clojure
(defn list-changer []
  (let [first []
       first (conj first 4 5)]
       (println first)
       (println first)))

Remember: we are binding, not doing assignments. The empty array that was identified by first does still exist. It will be taken care of, at some point, by the GC.

We have two ways to confirm that this is the case.

First, getting the array passed as a parameter to the function:

;; Clojure
(defn list-changer2 [first]
  (let [first  (conj first 4 5)]
  (println first)))

(def original [])

(list-changer2 original)

(println original)

Once you execute the above code you will get in the console and output of [4 5] for the array binded by first and then [] for the array binded by original. original has not changed outside the function.

The same code on Java will modify the outside list.

Another simple example to verify this is:

// Clojure
(defn list-changer3 []
  (let [first [1 2 3]
        second first
        second (conj second 4 5)]
       (println first)
       (println second)))

first has values “1 2 3”, all the way through. second will have values “1 2 3 4 5”

What makes immutability special?

If you have not programmed before on a FP language this is a question that you will (should) ask.

And the answer is the predictability of functions. If there are no side effects, and the function result depends only on the parameters passed you can know exactly the results of the function at any given point.

With predictable functions, testability and formal verification are much easier on a FP language than on an OO or Procedural language. Integration tests are far less important as there are less possibilities of an external influence.

Also, because of immutability, concurrency becomes easier. There are no locks that need to be applied to your code. There will be no values changing midway through the execution that are not controlled. Let me point out here that, if you are accesing an external resource, like a database, locks are still be needed. But otherwise, the compiler/OS can do some wise decisions to use multiple cores on a application. With the current limits to clock speed of CPUs, this is a very important characteristic.

Are there drawbacks?

One will think that not having state is a great idea (and indeed it is). Though there are drawbacks to it. The main one is performance. Mutable objects/structures allow for much quicker processing. There are tricks that can be done on the compiler of a FP language (I do recommend to see some of Rich Hickey’s talks about how Clojure was set up), to alleviate the issue.

Also, as with any other paradigm, there are situations on which FP does not map easily to the domain.

Conclusions?

Right now, I’m leaning towards the idea that immutability should be your default way of working, and then move to mutable state only when performance is not good enough.

Go Back to Into Adventure Intro

References

[1]Functional Programming: What?Why?When? back up