17 September 2011

What’s Wrong with Lisp

Lisp is a brilliant language. You can do things in it that are impossible in other languages, and that’s wicked cool. But though I think there is an upper limit to language expressiveness, I don’t believe Lisp represents that limit. Here’s why.

A quote I see from time to time—and for some reason have been seeing a lot lately—comes from the author of perhaps my favourite book, Le Petit Prince, the marvelous Antoine de Saint-Exupéry:

“A designer knows he has achieved perfection not when there is nothing left to add, but when there is nothing left to take away.”

In short, minimalism = perfection, and Lisp (well, Scheme anyway) favours it: uniform syntax, a few special forms, and then it’s turtles all the way up.

But even though I’d be the first to say that there’s a lot to take away from a thorough study of Lisp, there’s also a lot that could be taken away from Lisp itself, and for that reason I can only recommend it as an intermediate step in a programmer’s journey to linguistic nirvana.

See, this may come as a shock, but Lisp is not the most powerful language. Just as there are countless things that are ugly in most languages but mundane (if not beautiful) in Lisp, so too are there things that are ugly in Lisp and beautiful in other languages. They’re easy to see when you go looking for them.

It all boils down to the ability to concisely represent patterns. When a programmer in language X sees repetitive code, he refactors it using the tools available in X. To the programmer of some better language Y, even the most beautiful solution in X is imperfect, because he sees it as a workaround for the fact that X does not have what Y does. A Band-Aid solution, if you will.

Lisp macros are seen as the final word in abstraction and beauty. See repetitive code, make a macro, repeat. But aren’t macros the same kind of Band-Aid solution that exists in every language? Lisp just has nicer Band-Aids. (Ones with spaceships and unicorns on them.) And make no mistake, there is a language with nicer Band-Aids than Lisp.

To paraphrase Paul Graham: when you look down the spectrum of programming language power, you know you’re looking down; but when you look up, all you see are weird languages. Lisp programmers look down on all other languages—but what languages look weird to them?

I advise Lisp programmers to get off their pedestal and look through the weird, the esoteric, and the insane, to reach the power of what lies beyond. Question your assumptions. Why the oh-so-important parentheses? Simple: nesting! So why do you need nesting? To create scope. Why do you need scope? Variables, of course. So why do you need variables? To store values, obviously. What is the purpose of a value? To model state. Why do you need state? You don’t.

If you model everything in terms of data flow, for example, you no longer need state or any of the other things that come with it. You can write your program solely in terms of function composition. You don’t even need scoping (lambda abstraction) or application at all. You don’t need an absurd Haskellian type system to keep things functionally pure, either. 

The point is that lambda calculus is only the beginning. It is (for lack of a better term) the assembly language of functional programming. A language that directly models the operations of a Turing Machine is plainly rather boring, but what baffles me is that nobody seems to notice that the same is true of one that models lambda calculus. Lisp dresses it up, but only very little. We can do much better.

Let us take more away, to reveal what more is yet to be.


  1. Can I ask what you were thinking when you envision a language that is purely data flow? How would it be easier/better to work with?

    1. For one, purely dataflow languages make concurrency easier to deal with.

  2. @Anonymous:

    Forth and its relatives (such as Factor) come very close to what I consider ideal in a minimalist language. But the point of this post is not to name a single language or family, but rather to inspire critical thought about languages.

  3. First of all “A designer knows he has achieved perfection not when there is nothing left to add, but when there is nothing left to take away.” and your "minimalism = perfection" statement are assumptions, and what "perfection" really means varies to a great extend.

    "... To store values, obviously. What is the purpose of a value? To model state. Why do you need state? You don’t." You can do it via stack without naming values. If stack gets confusing you can use combinators. If stack gets more confusing you can refactor.

    Or you can use lexically scoped variables to name things. You have state that you don't really need now. But it is not the scary state of imperative languages.

    What about reading code? If you don't know the stack effect of every single word in the code block you are reading, is it possible to keep managing the stack in your head so you can keep reading?

    I am both a Lisp and Factor user. If I port a function from lisp to factor, I get rid of state and meet stack, and stack shufflers. Luckily factor provides me combinators, I abstract shuffling with combinators. But they bring a more applicative style. Do I need combinators? No, I don't. Wait a minute, I need combinators. They provide me ways of managing stack without losing my mind. Otherwise, I don't really need them.

    My personal motto on the issue is from Einstein, "Everything should be made as simple as possible, but not one bit simpler."

    All comes down to priorities and every choice has a cost. Postfix concatenative languages bring a unique and powerful approach to expressing programs, so does Lisp. I refuse to argue one is closer to "perfection". I am ready and willing to argue both are much closer to perfection than mainstream languages of today.

    By the way, I didn't know lispers were on that pedestal because lispers were the ones to channel me to Factor the first time. They told me it has many goodies of lisps in a concatenative style. For that reason Lisp community is the last one that needs to be suggested to get off that pedestal.