Tuesday, April 17, 2007

I've met lots of people who complain about Lisp and lots of people (especially Lisp folks) who complain about Python. Lisp is very elegant. There's something nice about its syntax (don't laugh!). The uniformity lets you do all sorts of neat things once you have macros. The basic syntactic construct in Lisp is the list, (a b c …) , and it can mean lots of things:

Sometimes (f x) is a function call, and f is the name of the function, and x is evaluated as an argument. Sometimes (f x) is a macro invocation, and f is the name of the macro, and x may be treated specially (it's up to the macro to decide). Sometimes (f x) is a binding. For example, (let ((f x)) …) binds a new variable f to the value x . Sometimes (f x) is a list of names. For example, (lambda (f x) …) creates a function that has parameters named f and x . Sometimes (f x) is a literal list. For example, (quote (f x)) . Sometimes (f x) is interpreted in some other way because it's enclosed inside a macro. How it's interpreted depends on the macro definition.

The ability to use the same syntactic form for so many different things give you great power. You can define all sorts of cool things this way. I'm writing a pattern matcher that uses list expressions to define patterns and macros to interpret those list expressions. Macros are great for writing elegant, concise code.

The trouble is that you can't easily tell just by looking at (f x) how to interpret it. It could do anything. You'd think maybe a text editor like Emacs (which uses Lisp as its native language) would be able to help you in some way. But no. Emacs can't tell either. So how can you, the person reading the code, figure it out? Well, you can, but it takes a lot of effort. You can't determine the syntactic meaning of code (e.g., whether it's a definition or an expression) by looking at the code locally; you have to know a lot more of the program to figure it out. Lisp's syntactic strength is at the same time a weakness.

Python on the other hand has no macros and doesn't give you much to write concise, abstract, elegant code. There's a lot of repetition and many times it's downright verbose. But where Lisp is nice to write and hard to read, Python makes the opposite tradeoff. It's easy to read. You can determine how to interpret something—a string, a list, a function call, a definition—just by looking at the code locally. You never have to worry that somewhere in some other module someone defined a macro that changes the meaning of everything you're reading. By restricting what people can write, the job of the reader becomes easier.

Lisp seems to be optimized for writing code; Python seems to be optimized for reading it. Which you prefer may depend on how often you write new code vs. read unfamiliar code; I'm not entirely sure. What bothers me the most though is not that these two languages do different things, but that the people who argue about it seem to think that there is one “best” answer, and don't see that this is a tradeoff. When I'm writing code I prefer Lisp; when I'm reading code I prefer Python. I think this is an inherent tradeoff—any added flexibility for the writer means an added burden for the reader, and there is no answer that will be right for everyone.

–Amit

P.S. When I read debates online, I have a bias towards the people who view these things as tradeoffs and a bias against the people who say there's only one right answer and everyone else is stupid or clueless. This has sadly pushed me away from Lisp, the Mac, and other systems that I think are really good but have fanatical communities. When you're in a debate, consider that the other person might not be stupid, and there might be good reasons for his or her choices. You'll not only learn something about their position, but you'll be more likely get people to listen to you and adopt your point of view.

Update: Also see reddit comments; [2012-06-29] Rich Hickey calls multiple uses of parentheses “overloading” in this talk; [2018-10-12] Also see Heinrich Hartmann's blog post.

Labels: programming