On the Relationship Between Python and Lisp

Lisp was way ahead of its time. Because Lisp was the first high-level programming language there is a tendency to believe that therefore everything good done since then is a reinvention of Lisp. Java is Lisp. XML is Lisp (no it isn't). And now we have Python is Lisp.

Here is the story as presented by Paul Graham (a famous Lisp expert). Of the three languages he chooses to discuss, Java, Perl and Python, Python is considered cooler (if not more popular) than Perl and Perl cooler (if not more popular) than Java because Python is most like Lisp of the three. Unfortunately, Python is a sort of immature Lisp which may over time grow into its full Lisp-yness but why wait around when you could just be using Common Lisp today?

I'm sure if you are a Lisp programmer, it is a compelling story. Unfortunately it does not ring true.

Graham says that Perl is cooler than Java and Python than Perl. In some circles, maybe. Graham uses the example of Slashdot, written in Perl. But what about Advogato, written in C? What about all of the cool P2P stuff being written in all three of the languages? Considering that Perl is older than Java, and was at one time the Next Big Language, I think you would have a hard time getting statistical evidence that programmers consider Perl "cooler" than Java, except perhaps by virtue of the fact that Java has spent a few years as the "industry standard" (and is thus uncool for the same reason that the Spice Girls are uncool) and Perl is still "underground" (and thus cool, for the same reason that ambient is cool). Python is even more "underground" than Perl (and thus cooler?). Maybe all Graham has demonstrated is that proximity to Lisp drives a language underground. Except that he's got the proximity to Lisp argument backwards too.

Graham himself demonstrates (in his example) that Perl's closure semantics borrow much more heavily from Lisp than does Python. So where is the evidence that Python is closer to Lisp than Perl? In fact Perl has much more consciously borrowed features from Lisp than has Python. Perl's Lisp-like facilities are also loved by the Perl community whereas Python's community has had a love-hate relationship with theirs. More on this in second.

Python is not growing towards Lisp. Python's most directly Lisp-inspired features were added very early in Python's lifetime. Guido was a new language designer then and had not mastered the habit of saying "NO". When people told him that he could borrow these cool features from another language he did so without entirely thinking through the consequences. For the first several years the features were an extremely bad fit. Other improvements to Python made them a slightly better fit more recently, but at the same time, other new features in Python have made them less important and less useful.

In fact, there are proposals (not yet high priority ones) to deprecate some or all of "apply" (which has a first-class syntax now), "map" and "filter" (which can always be replaced with list comprehensions), "reduce" (which can be replaced with for-loops) and "lambda" which can be replaced by named functions. These will not be removed tomorrow (don't send me hate mail). Nevertheless, the fact that they are even being discussed for removal indicates that there is no interest in making Python more Lisp-like - rather it would be preferable to make it less so. (at the very least, the borrowing of the name "lambda" was, IMO, a horrible mistake!)

Python is growing, but not towards Lisp. As Python becomes more popular, I expect advocates of other languages will try to claim it as a descendant of theirs (call it "Alexander Graham Bell is Canadian" syndrome). Python is really a little Lisp, says Graham. A Haskell programmer could claim that Python is really a little Haskell, thanks to its support for List Comprehensions and some lazy features. An Icon programmer could claim that Python is getting to be more like Icon with the addition of lazy generators. A Smalltalk programmer would recognize metaclasses, the unit testing features and probably the new method resolution order. The warning, logging and exception handling infrastructures are probably closest to Java. In ten years, Python will probably have stolen more ideas from these languages (including Common Lisp) and they may even have stolen some back. But if you expect Python to grow towards any particular one of these, you'll be waiting a long, long time.

In the last several versions of Python, Lisp has the least direct claim of all. The only newish feature that I can imagine as being Lisp inspired is nested scopes. But C, C++, Java, Perl and Ruby all have nested scopes. This is only a "Lisp feature" in the sense that McCarthy's Lisp (not Common Lisp!) was the first high level programming language and all languages since have necessarily borrowed from or reinvented something pioneered there.

Look at Graham's list of "what made Lisp different." He shows that Python has picked up roughly five of them. C had the first three (conditionals, function pointers, recursion) thirty years ago. The next two (dynamic type checking and garbage collection) were also available in languages back then, but were considered too CPU-wasting to be mainstream until recently. The next three are the ones Python does not implement. What is interesting about them is that they have no real hardware cost. They are merely features that the programming language world in general, and Guido in particular, have chosen to reject.

Programmers do not like deeply nested expressions. They like a language that encourages a style where expression results are assigned names. A statement/expression distinction encourages (and in some cases requires) that. A symbol type is not a bad idea but the marginal gain over interned strings is minimal. And the Lisp S-expression notation has been loudly and explicitly rejected over the last half century.

In short, Graham predicts that because modern languages "grew into" some Lisp features that were at one point too computationally expensive to be mainstream, they will one day "grow into" the very features that prevented Lisp from catching on, even once the hardware and compiler techniques had caught up.

I'll reserve judgement on the last feature on his list. Python implements two out of the three modes of evaluation he describes: evaluation at runtime and reading at runtime. The last is evaluation at compile time (macros). Python could one day grow a macro feature, perhaps based upon Dylan's. There are still things Python can learn from languages in the Lisp family (and Smalltalk, and Haskell, and Icon, and E, and Oz, and ...). The secret is to take what's successful and leave the rest. If you have chosen not to be a Lisp programmer, then you know what I mean.

The "Accumulator" Micro-benchmark

Graham demonstrates the "power" of Lisp compared to Python by comparing tiny code snippets. Just as it is unsatisfactory to program FORTRAN in C, he demonstrates that it is unsatifactory to program Lisp in Python. No surprise there!

Rather than presenting a real-world problem, "create a 'thing' that can accumulate a value and return the value on request", Graham demands the 'thing' be a function. Obviously this will work best in a language where functions are the central abstraction mechanism. But Python has both functions and objects as primitives. In Python, the appropriate abstraction mechanism is an object, not a function. An object is a better for choice for a variety of reasons:

Using conventions like those described above helps Python programmers to reason about each other's code. "No input arguments? That function is probably stateless." "Function returns a value? It is probably side-effect free." By the way, it is this kind of reasoning that shows why it is good for languages to have BOTH objects and functions as first-clsss types

As soon as Graham stated the problem in terms of: "create a function that has internal, updatable state" he had stepped out of the object oriented model of Python (and out of the model of pure functional programming as well). Although Python does have a variety of ways to create functions with mutable state, no Python programmer would (or should!) think of the problem in those terms.

It is therefore not surprising that Python has poor syntactic support for that formulation. Here is the idiomatic Python equivalent:

class Accumulator:
    def __init__(self, start):
        self.val = start

    def incr(self, amount):
        self.val += amount

    def decr(self, amount):
        self.val -= amount

    # not strictly necessary but makes some programmers warm and fuzzy
    def getValue(self):
        return self.val

val = Accumulator(5)
val.incr(10)
val.incr(11)
val.decr(5)
print val.getValue()

If I had restricted myself the functionality of the Common Lisp version it would have been roughly five statements/lines of code (compared to roughly five logical statements for CL).

Some Lisp geeks may never like Python. But you might.

Yesterday my wife (a new photographer) said "it seems like many (most?) of the professional photographers I know use Macintoshes." I couldn't resist the opportunity to engage in a little battle of the university degrees. "Yeah, Macintoshes have always been popular with the artsy crowd. Lots of pretty buttons to click." Later that day, speaking more seriously, I told her: "if you think a Macintosh would help you in your photography, we could get one. I would like to have a look at the Unix framework embedded in it anyhow. Macintoshes are getting really popular with geeks." She responsed: "I thought you said Macintoshes were only for artsies?"

When I made the first statement I was in a 1990s thought pattern. In the 1990s it was common to think that an operating system could be easy to use OR sophisticated but not both. But at the end of the century this dichotomy became less and less meaningful. Linux grew shiny buttons. Windows got ports of Perl, GCC, bash and the other Unix tools. And then Macintosh OSX came out and did the most impressive job of being both a family sedan and a race car at the same time. Python is the Mac OSX of programming languages. It is designed to be used and loved by new programmers, "average" programmers and language geeks all at the same time.

In his various writings, Paul Graham has strongly suggested he doesn't think this is possible. He uses the automobile analogy and says that his new language, Arc, is designed without apology to be a Porsche. It will be harder to use at first but more "powerful" in the long run. The underlying assumption is that it isn't possible to make a language that is both easy to use at first and more powerful in the long run. After all, if a thing goes without being done for long enough, there grows a sense of its impossibility.

A family sedan/race car is impossible because of the laws of physics. But very little is impossible when it comes to software design. Here is how you design a language or operating system or word processor that appeals to all three classes of users:

This last point might make you say: "aha, so you can't be all things to all people, just as I suspected." That's true. You cannot. When I boot my new Macintosh, I will probably be offended by the CPU time wasted to make the icons pulsate. But the point is that the advantage of being able to use the same computer as my wife outweighs the minor irritants that come with using a computer that was designed for both of us.

Similarly, consider the advantages to using Python rather than a larger and more complicated language.

Tim Berners-Lee spends all day thinking grand architectural thoughts about the Web and negotiating its dangerous political waters. When he wants to hack he wants to use a language that can fit in a single "page" of his brain so he can swap it entirely in and then out again. Python is that language. Thanks to the fact that I also use Python, I can reuse his code when I want to do semantic web processing (just as I can reuse my wife's IMac to test some Unix code I'm writing). Paul Graham says that a language designed for "the masses" is actually being designed for "dufuses". My observation is that some of the smartest people on the planet are dufuses when it comes to programming, (or in some cases just dynamic programming languages) and I am pleased to share a language (and code!) with them.

I usually spend a big chunk of my day in Python. But most professional programmers will not be able to do that until Python is a dominant language. In the meantime they must switch back and forth between Python and the language of their day-job. Python is designed to make that easy. During the day they can pound out accounting code and at night switch to hacking distributed object oriented file systems. Every intuititively named function name makes it that much easier to "swap" Python back into your brain. After we have been away from a language for a while, we are all dufuses, and every choice made in favor of the duffers actually benefits even high-end programmers.

I get paid to share my code with "dufuses" known as domain experts. Using Python, I typically do not have to wrap my code up as a COM object for their use in VB. I do not have to code in a crappy language designed only for non-experts. They do not have to learn a hard language designed only for experts. We can talk the same language. They can read and sometimes maintain my code if they need to.

From a purely altruistic point of view, it feels good to me to know that I am writing code (especially open source code) that can become a lesson for a high school student or other programming beginner. I like to give programming classes to the marketing departments at the companies where I work.

Even hard-codre geeks get a certain aesthetic pleasure out of using something that feels minimal and simple because most of the complexity has been hidden away or removed.

If there were a high price for compatibility with low-end programmers, then maybe these benefits would not be enough to win me over. But Python is demonstrably popular among people with deep experience in other languages, including Lisps. Examples include Eric Raymond, Peter Norvig, Dan Connolly and Erann Gat. I know Scheme and Python, but not Common Lisp. Paul Graham knows Common Lisp and Scheme but not Python. Don't trust us. The people I've listed above know at least one Lisp (typically Common Lisp) and Python. They have no axe to grind. They don't seem to think they have lost something major by switching to Python.

Although Python is still not one of the most popular languages, it seems as if it is gaining popularity quickly, although it has no marketing machine behind it, no large corporate sponsor and is not embedded in a killer app 1(as Java and JavaScript were embedded in Netscape). Ruby is another language experiencing strong growth based on customer satisfaction. Perl may rejoin this list when Perl 6 is released.

More and more programmers of all levels of sophistication seem to think that "Python gets its compromises just right". If you aren't willing to wait around for Lisp to take over the world (again) then I'd suggest you investigate Python or one of the other so-called "scripting languages". I admit that Python will grow and change as you are using it. I also admit that it grows relatively organically, so nobody (including Paul Graham) can predict what, exactly, it will grow into.

1. Zope may be an app and it may be killer, but it is not a killer app for Python in the sense that there was never a time where the majority of Python programmers were using Zope, in the way that the majority of PHP users use Apache, or the majority of JavaScript (and early Java) programmers use(d) a Web browser.