(Or, Python vs Clojure rant)
This post assumes some familiarity with Lisp and Python from the reader.
Like loads of programmers, I have read most of Paul Graham’s essays, and I always finish them with that feeling that if I’m not using Lisp then I must be an idiot. Like most programmers, I certainly don’t want to be an idiot, so I went ahead and learned Lisp, Common Lisp.
At first, I was horrified by all those damned parentheses. Still, I kept at it until I was able to write code half-decently. I didn’t get very far, and truly diving into Lisp was buried deep into my to-do list.
I didn’t really got Lisp until I learned about Clojure.
Clojure is a new functional Lisp that runs on the JVM. It is a great language with brilliant design and it has the potential to bring some great research ideas into the mainstream.
Learning Clojure has gotten me to really appreciate Lisp for what it is.
Some months ago I wrote about how I loved LINQ. With Lisp, I can’t complain if I don’t have LINQ, I can just write a macro to implement it.
I understand that when I’m writing Lisp code, what I am writing is an abstract syntax tree (AST) in human-readable form. You can see code as the very data structure that represents your program, and you can modify it as such. That is why Lisp must be just the perfect language for Genetic Programming, where code needs to modify itself.
I love the fact that in Lisp everything is an s-expression. S-expressions have an advantage that I hadn’t anticipated. They make editing easier in an editor like Emacs where you have commands that explicitly handle sexps.
Editing Lisp inside Emacs, after a while, is a very enjoyable experience. You can transpose,kill,navigate through and mark sexps as if they were letters or words. And since everything in Lisp is an s-expresion, it becomes very malleable.
So, after having my Lisp epiphany, I started seeing Python through a new light. I saw that Python was just like Lisp in so many ways, yet it was different in many others.
Python is dynamic and has a REPL. Both are huge advantages that Lisp has had for decades.
In Python, there is a difference between statements and expressions. This was my first big no-no on my comeback to Python.
I would like to write something like this:
a = if (x == 2): "Hello" else: "Goodbye"
Lisp code is elegant, and Lisp as a language is mathematically beautiful. It is also true that once you get used to all the parentheses you start not to notice them. However, Lisp is just not as pretty as Python. There’s only so much you can do when your language requires the programmer to write the AST directly.
Python has the most beautiful syntax I have yet to see. Letting whitespace have syntactic meaning is a great design decision. The resulting code is indented the way people should indent their C-style programs anyway.
I have found downsides to this approach. In programs with lots of consecutive, horrible OpenGL API calls, things would look prettier if the language would let you indent at will. 99% of the time, however, it is a feature rather than a bug.
Every programmer has some personal pseudocode. This pseudolanguage is the language we think in. Python is as close to most programmers’ pseudocode as you can get. I get this warm, fuzzy feeling when I code in Python. Its philosophy dictates that programming is so hard that the language should really just get out of the way of the programmer.
Programming in Clojure has definitely changed the way I program. More thanks to the fact that it is functional than to the fact that it is a Lisp. After you pass the brain-freeze that is inevitable when dealing with lack of state, you reach a point of enlightenment. That is that you realize that programming functionally frees you from worrying about post-conditions and pre-conditions. It is such a nice feeling when you know that a function isn’t really changing the world, that it will change the way you code in other languages. Being stateless lets you stop worrying about a huge set of problems. It makes you smarter, since you are keeping less things in your head, and it reduces the bugs you can create. I am not saying you’ll absolutely loathe state after spending time with a functional language. I love a for loop as much as the next dude, I am saying that you will avoid state when you can. I agree with Tim Sweeney in that the functional paradigm should be the default. And that modifying state should be made explicit. I think this concept is present in Clojure’s STM approach, although it hasn’t really been proven effective in huge, complex applications.
I still prefer to code in Python than to code in Clojure, just like I still prefer to speak in Spanish, my native language, than to speak in French.
When I code in Lisp, I write pseudo-code that is pretty similar to Python and then I translate to Lisp as I write. Yet now, when I’m writing in Python, I often say “This would could be easier to express with Clojure”. I would like to know if my pseudolanguage is a product of having been exposed to imperative programming all these years. I don’t know if there are programmers out there whose pseudo-language is functional. Maybe it’s just human nature to think imperatively.
If my pseudolanguage ever starts to look like Clojure, I guess I’ll have my answer