The Unofficial Guide to Rich Hickey's Brain
Part of my excitement in learning Clojure has been being exposed to Rich Hickey's thoughts on programming. Rich Hickey has a clear, consistent way of viewing fundamental programming concepts that I think any programmer would benefit from. Every time I watch one of his talks, I feel like someone has gone in and organized my brain.
In this article (and more to come (possibly)), I begin my attempt to catalog Mr. Hickey's unique viewpoint. Eventually, I would like to produce a concise summary of his ideas. My hope is that this will provide an easily-scannable reference to Clojurists and an accessible introduction to non-Clojurists.
What follows is derived from Rich Hickey's talk, "Are we there yet?"
Today's OOP languages - Ruby, Java, Python, etc. - are fundamentally flawed. They introduce accidental complexity by building on an inaccurate model of reality. Where they have explicit definitions for the following concepts, the definitions are wrong:
Below, we'll contrast the OOP viewpoint on each of these topics with the Functional Programming viewpoint. But first, we'll compare the models of reality which underlie OOP and FP. It's this underlying difference which gives rise to their different approach to the topics above.
Metaphysics, Programming, and You: Comparing OOP and FP
When talking about metaphysics things tend to get a little fuzzy, but hopefully this will all make sense.
As the ever-trusty Wikipedia explains, metaphysics attempts to answer two basic questions in the broadest possible terms:
- What is there?
- What is it like?
Rich Hickey explains the difference between OOP and FP metaphysics by contrasting their explanations of what a river is.
Object Oriented Programming
In OOP metaphysics, a river is something which actually exists in the world. I know, I know, I can hear what you're saying: "Uh... yeah? So?" But believe me, the accuracy of that statement has caused many a sleepless night for philosophers.
The wrinkle is that the river is always changing. Its water never ceases to flow. In OOP terms, we would say that it has mutable state, and that its state is ever fluctuating.
The fact that the state of the River Object and that Objects in general are never stable doesn't stop us from nevertheless treating them as the fundamental building blocks of programs. In fact, this is seen as an advantage of OOP - it doesn't matter how the state changes, you can still interact with a stable interface and all will work as it should. An object can change, but to all observers it is still considered the same object.
This conforms to our intuitive sense of the world. The position of the electrons of the coffee in my mug matters naught; the coffee still interacts with my taste buds in the way I expect.
Finally, in OOP, objects do things. They act on each other. Again, this conforms to our intuitive sense of the world: change is the result of objects acting upon each other. A Person object pushes on a Door object and enters a House object.
In FP metaphysics, we would say that we never step in the same river twice. What we see as a discrete thing which actually exists in the world independent of its mutations is in reality a succession of discrete, unchanging things.
The "river" is not a thing in and of itself; it's a concept that we superimpose on a succession of related phenomena. This concept is very useful - I won't belabor that point - but it is just a concept.
What really exists are atoms (in the sense of atomic, unchanging, stable entities) and processes. The river is not a stable object; rather, it is a succession of related atoms which are generated by some kind of process.
These atoms don't act upon each other and they can't be changed. They can't do anything. Change is not the result of one object acting on another; change is the result of a process being applied to an unchangeable atom. To say that something has changed is to say, "Oh, there's a new atom in this stream of atoms." It's like saying that HEAD points to a new commit in your Git repo.
OK! Enough with metaphysics. Now let's describe the more immediately useful topics, starting with Value.
It's obvious that numbers like 3 and 6 and 42 are values. Numbers are stable, unchanging.
It should also be obvious that OO languages have "no proper notion" of values in this sense. As Rich Hickey points out, you can create a class whose instances are composed of immutable components, but there is no high-level concept of immutable value implemented as a first class construct within the class.
This is one of the main causes of headaches when doing OOP. How many times have you pulled your hair out trying to figure out how an object's attribute got changed? The fact is, in OO languages there is no built-in mechanism to ensure that the object you're dealing with is stable.
This is the big reason why concurrent programming is so difficult. Even in single-threaded programs this is a problem, and it's one of the reasons why we develop sprawling test suites. You can't be sure if a method call on your Toupee object is somehow going to cause a change in your HipsterGlasses object.
By contrast, in FP languages emphasis is placed on working with immutable values. Since these values can't change, a whole class of problems simply disappears.
In the video, Mr. Hickey says:
The biggest problem we have is we've conflated two things. We've said the idea that I attach to this thing that lasts over time is the thing that lasts over time.
In FP, identity is essentially a name we give to a sequence of related atoms. "River" refers to the sequence R1, R2, R3, etc, produced by the river process. This is directly analogous to HEAD in Git - it's just a name which is used to refer to actual values. In OO, there really isn't such a distinction.
Or as the man himself says,
Identity is a putative entity we associate with a series of causally related values (states) over time. It's a label, a construct we use to collect a time series.
In OO, there is no real clear definition of state. Maybe it's, "the values of all the attributes within an object right now." And it has to be "right now", because there's no language-supported way of holding on to the past.
This becomes clearer if you contrast it with the notion of identity in FP. In the Hickeysian universe, a State is a specific value for an identity at a point in time. (For me, this definition really clarified my own thoughts.)
There's no real notion of time in OO. Everything's just "right now". This is part of what causes problems in concurrent programs.
By contrast, in the FP worldview we've been exploring, time is well-defined. Time is a by-product of ordering states within an identity.
(By the way, I'd really like to write more here, and would appreciate any suggestions.)
Finally, behavior. I don't have too much to write here - I might need to watch some more talks - but I'll include Mr. Hickey's thought-provoking observation:
There is no behavior. When you get hit by lightning, who's behaving?
This may be what inspired Steve Yegge's post, Execution in the Kingdom of Nouns.
Well, that's it for now. I hope you've found this post useful. If you have any suggestions, I'd love to hear them!