Clojure is Imperative

Summary: Clojure is imperative as opposed to declarative. Its operations are clearly defined in terms of concrete actions, even when those operations are abstract. This is in contrast to many languages which are more declarative. However, Clojure being imperative is related to how easily it can be bootstrapped.

Update: أخلاق الخيميائي pointed out that I was wrong about the size of GHC. Luckily it was not salient to my point so I just removed that part of the article.

I was recently on the Cognicast and I mentioned something really important to me, but I did not go that deep into it.

Clojure, and Lisps in general, are imperative languages (as opposed to declarative). Yes, they are good for doing functional programming, but their main paradigm is executing lists of commands in order.

On the podcast I mentioned the first imperative example that came to mind, which was the do form, which executes each expression in the body and returns the value of the last expression. You would only want to execute an expression and throw away its value for its side effects.

Another deeper example is the way Clojure deals with top-level forms. By top-level forms, I mean all the defs and defns. These are executed in order. On the surface, you may think "I'm defining a function called foo". But defn is really doing a list of things, in order: it's checking if the var foo exists already in the namespace. If it doesn't, then it creates it, modifying the namespace. Then it sets the root binding of foo to the function.

This might seem obvious, but look at languages like C or Haskell. In C, top-level forms are declarative. You are not telling the compiler to perform certain concrete steps. Instead, the compiler reads that you want a function called foo. It's the compiler's job to know how to build that into the program and export it out of the module.

Clojure, in fact, projects a very thin illusion of declarative expressions. Within certain limitations (like that the order of your defns matters) you can treat defns as declarative. They're declarative if you squint.

Here's the code for defn. It does a few things. It handles metadata and some other details. In the end, it converts a (defn foo ...) into a (def foo (fn ...)).

def is one of the few special forms, so it is implemented in the compiler. But there are so few special forms! Everything else can be written in the language itself. Write the core in machine code, and the rest in Lisp.

And this gets to the heart of it: you can write a Lisp yourself. Many people have. You could write a declarative language compiler and keep adding to the compiler over years and years. Or you can write an easy Lisp compiler in a weekend and build features on top of it, almost never having to change the original compiler.

This is the magic of bootstrapped languages like Lisps. They have a small core that you need to get right, then everything else can be written in that core. It's the ultimate minimal virtual machine.

What's the relationship between bootstrapping and imperative? A truly declarative language must push complexity into the compiler. The more declarative a language, the more the compiler must do to maintain the illusion. By letting the imperative underpinnings shine through the small core, you are continually able to work at the fine grain necessary to continue bootstrapping.

I like Lisps (and Clojure) because I feel that I can understand them. I don't actually understand everything, but I could if I tried. Declarative languages can be very powerful (and I like many of them). But somewhere along the way I developed a deep interest in bootstrapping. Bootstrapping is compounded leverage. You build small abstractions on top of the previous ones, and use those to build yet grander ones.

If you like this attitude toward programming languages, you should learn a Lisp. I suggest Clojure, and I recommend the LispCast Introduction to Clojure video series. You'll learn about building up powerful abstractions, one layer at a time, in a small amount of code.

Learn Functional Programming using Clojure with screencasts, visual aids, and interactive exercises
Learn more

You might also like