Sunday, October 16, 2005

Why I never got into Lisp

Like many programmers (I suspect) who hear enough Lisp afficionados talk about how great their language is, I've given Lisp a try. Twice. I didn't make much headway on either occasion.

For various reasons I was poking around python/lisp in google tonight and I came across this thread from a couple years ago. This crystalized it for me:

now compare
        if x > y or a == 0:
            a1 = m * x + b
            a2 = m / x - b

        (if (or (> x y ) (zerop a))
            (setq a1 (+ (* m x ) b )
            (setq a2 (- (/ m x ) b )).

In this case, Lisp requires about 33% more typing, almost all parentheses.

And, oh yeah, I forgot, aside from the parentheses, the pervasive prefix notation can be a drag.

Syntax does matter. Everyone who has run screaming from a steaming pile of Perl understands this. Lisp's syntax isn't the train wreck that Perl's is; at the very least it's consistent. I could get used to it if there were no alternatives.

But Lisp syntax was designed 50 years ago to make it easy to parse! We don't have to optimize for this anymore -- it's time to optimize for the programmer!

Today, Python and others have the features that made Lisp powerful without the syntactical baggage. Python has its own advantages, too, such as one of the best-designed standard libraries around. (Hence Paul Cannon's Lisp-to-python-bytecode compiler.)

Further reading: On the Relationship Between Python and Lisp.


beza1e1 said...

I'm mostly doing python stuff, but now i'm looking into Lisp. Ok, it is mainly because of Paul Grahams smart essays.

As pointed out often. Macros are the one thing Python lacks to have Lisp power. Macros mean meta-programming and in this case parentheses seem to be the only way.

In comp.lang.lisp there was this one, which explains, why Lisp is not for beginners: "The beginner may have a hard time grasping that the parentheses are not training wheels and are not for him. The point that they are there to help industrial teams exploit meta-programming probably goes right over his head."

I made a simulation for a game i develop and out of boredom i made a Python and a Scheme version. The code is optimized for easy changing of the game mechanics/calculations.

While simulations are normally the native realm of OOP. Scheme turned out to be more in my way of thinking.

Check out the two programs:

Jonathan Ellis said...

Lisp people love to go on and on about macros, since it's pretty much the one thing that remains unique to them. (With the exception of other even-more-fringe languages like Dylan.)

I think that the rather profound lack of interest in macros in the rest of the industry constitutes the best reply. For 99.99% of applications, you don't need macros. (Peter Norvig, a well-known Lisp guy, explains why:

For that 0.01%, I guess you're welcome to use Lisp. Just as you're best choice for a Linux device driver is probably C. No one language is best for everything... but Python seems to hit a sweet spot.

Anonymous said...

I think your post is missing some important points:

- LISP remains a more powerful language than Python, not only because of macros, but because of the fancy ways it lets you control evaluation (partial quoting, etc.).

- LISP syntax was not just "designed 50 years ago", there is something fundamental and minimalistic about it. In other words, it isn't "just syntax", it is also program structure, there is not conversion to an AST. This presents some advantages for low-level hacking.

- Some LISPs use dynamic binding (which is a different concept than dynamic typing), whereby the variables of the caller are available in the environment of the callee.

If only LISP had all the nice libraries that Python comes with, I personally would be using LISP. Also, understanding LISP can help you truly understand the nature of Python and computer languages in general, which is not true of Python IMO. Learning LISP is hard, simply because it is very rich and forces you to be explicit about everything (e.g. quoting), and because it blends the notion of language and libraries-- the language itself is just a thin core of the important most powerful concepts.

Juri Pakaste said...

I use several languages all the time. Yesterday, I used OCaml, Java and Python. I haven't used much Common Lisp or Scheme lately, but I'm relatively fluent in both.

I've pretty much abandoned hope of finding a language that hits any particularly sweet spot. Each has its strengths and weaknesses. Each helps you see things from a slighly different viewpoint and often highlights the good and the bad in other languages.

Using Python you get a practical highly dynamic language with tremendous library support.

On the other hand, Common Lisp shows you can have an extremely powerful dynamic language and still be fast. Common Lisp, Scheme and the other more or less functional languages also pretty well highlight the silliness of statements in a language (as opposed to expressions). Looking at a Scheme implementation with a module and object system you notice that Python isn't all that different after all. The ML family shows you don't have to have prefix notation to have everything work as expressions and that static typing doesn't have to suck (but you still miss the dynamic features every now and then.)

As for macros, I find them to be useful. I don't use them a lot in everyday programming with CL and Scheme, but I do use them, most often of the with-foo-bar kind. Python has acquired and continues to acquire features that lessen the need for macros, but I think that it would be easier for all to just add the generic feature instead of lots of small specific ones. It would help keep the size of the core language in control.

Chris Rebert said...

Have you heard of M-expressions?
They help somewhat, at least making Lisp look like C-style function calls instead of lists.
But still, your point remains quite valid. It's amazing how the verbosity of a language can have such a significant impact on its popularity. But since hackers are lazy (in a good way [see Hackers and Painters]), it's no surprise. Reminds me of COBOL - MULTIPLY A BY B GIVING C.
However, Dylan ( sounds like a nice compromise - ALGOL-like syntax with macros.
Overall though, I see little advantage to macros over Ruby/Smalltalk's anonymous blocks.
In short, YAGN macros, and hackers like terse languages, like DWIM.

paul cannon said...

Devious of you to invoke my name and make me comment!

Maybe the best way I can explain why I like macros (and thus, lispy syntax) is Paul Graham's comment that goes something like "the shape of a program should reflect only the shape of problem it's trying to solve". Meaning, if you see patterns in your code, it's probably a place where you could use a macro to more directly match the shape of the code to the problem. Then when something needs to be changed, there's always one place to change it. Basic computer science stuff, but beautiful things happen for me when I take it to the next level.

No, I never _need_ macros, or functions, or anything else, but they can sure improve readability, maintainability, and coding time.

Anyway, I'm sure it doesn't fit everyone's coding style.

Straw Dogs Python Blog said...

Commented on this post at my blog. I'm a firm believer and preacher in the belief - Every language has its place. Whatever tool fits the job use it.
Blindly supporting a language as 'the best' is silly and causes nothing but trouble.

Anonymous said...

"I think that the rather profound lack of interest in macros in the rest of the industry constitutes the best reply. For 99.99% of applications, you don't need macros."

Well, "the industry" ignored GC, bignums, closures, and a dozen other features that Lisp has, for *decades*. For a long time "the industry" standard was C; that does not mean it's a good idea.

Yes, you don't need macros for 99.99% of programs. In fact, you can write 100% of applications on a Turing machine. That doesn't mean there isn't something better available.

As for your original example: yes, when doing a bunch of arithmetic, s-exps are not the best syntax. A vanishingly small fraction of code I've written in my life consisted of line after line of arithmetic. And whenever I've had to do that in Lisp, I just whip out a reader macro that lets me bang out code that looks remarkably like your Python example.

That's another strength that Lisp has that I've yet to see another language match: if you don't like the syntax, write your own. Python is a neat language, but if its syntax isn't ideal for a particular application (like my current job), you can't adapt it to work better. And when you get beyond "a1 = m * x + b", it's really nice to have.

Henrik said...

Strange, I find that I need to write macros for 100% of my programs. Having several years of professional experience working with Python, I now use Common Lisp everyday instead. As I remember it, always when I wanted to use Python in a project about 99.9% of the customers and 70% of my coworkers thought it was a very odd thing, and I ended up having to do it in C++ or Visual Basic instead.
That thought me I am usually right and 99.9% are usually wrong. Python? A scripting language? We can't hire anyone who know it!

Anonymous said...

Macros are hardly the only unique feature Lisp offers.

- There is still no object system as powerful as CLOS, nor is there any other object system that allows you to redefine its semantics in certain situations to help match your application better (see the Meta-Object Protocol)

- No other language besides Smalltalk comes close to Lisp in terms of interacive development - i.e. working with a running Lisp program at all times and redefining code, classes, etc. as you work with it, instead of just playing with a static piece of text that gets fed wholesale into an interpreter or compiler. This has led to the development of powerful IDE's like LispWorks, Allegro, or (to a lesser extent) SLIME. And the IDE's that ran on the Lisp Machines of old were even more stunning. Because you are always working with a live Lisp image, you can create and inspect objects on the fly, browse class hierarchies, etc., all without having to give up that dynamism.

- You downplay macros because you can't have them in Python. But they really are a huge win, despite what Mr. Norvig might say (by the way, Norvig's book Paradigms of AI Programming is a superb way of picking up Lisp). For more evidence, read or skim through: focusing particularly on the use of the macro define-binary-class, which is not part of CL and was defined specially by the author.

- Your assertion that Lisp's lack of syntax is simply a result of making life easier for the parser is, frankly, ridiculous. Were that the case, surely someone would have imposed a better syntax over Lisp in the last 50 years. And, in fact, people have tried. But Lispers like myself actually think of the syntax as a *plus*. When you use a Lisp-aware editor, you will see how much easier it is to move through trees of code instead of the clumsy by-line and by-character motion that's the best you can do in most languages. The simplicity of the syntax is what allows macros to exist and is a crucial part of Lisp's aesthetic appeal (that last phrase might sound ridiculous to people still fixated on parentheses, but everyone has that reaction initially until they start writing and working with Lisp code)

- It's fast. Fifty years of experience writing Lisp compilers has paid off. Most Lisps compile to native machine code (a few compile to portable bytecode instead). You have incredible control over the optimization of hotspots in your code. For example, you can turn on static typing for specific variables to save the time spent on typechecks.

- Maturity. Although Lisp's open-source library situation leaves a little to be desired (but not as much as outsiders would have you believe), the language, its common idioms, etc. have been around for decades and have survived the test of time. Research at academic institutions and in industry has refined Lisp over the years.

- Miscellaneous features, like "special" variables, the LOOP macro, the powerful FORMAT function, infinite-precision interger arithmetic, distinct list and vector types (Python does not understand the distinction, AFAIK)

Your suggestion that Lisp has wrongly been allowed to survive for 50 years is, frankly, pretty arrogant. You admit that you haven't yet grasped the language, yet you are willing to defy fifty years of experience and the praise of some of the brightest minds in computer science (Alan Kay, Gerald Sussman, Guy Steele, Edsger Dijkstra, David Moon, and on and on) and let everyone know that Lisp is really just a lot of hot air.

Well, thanks for the insight, Mr. Ellis. Can I kindly recommend that you at least learn the language (and if it's not too much to ask, that you also complete a small-sized project with it, so you can see how Lisp's features really pay off for programming-in-the-large) before gifting the Internet with your thoughts on it? In exchange, I promise not to make disparaging claims about Python on the Internet until I've done something worthwile with it.


Anonymous said...

And just to clarify, Lisp's syntax had nothing to do with parsing. It was created as a mathemtical notation - a simple way of expressing algorithms. One of McCarthy's students decided to make a computer language out of it, against McCarthy's wishes.

The two wrote a syntax for Lisp that was meant to replace M-expressions, but surprise, surprise, no one was interested in it. Does it still sound like the syntax was a hack for parsing?

Anonymous said...

I use macros all the time.

Instead of:
(define (on-today) (void))
(define (on-calculate) (void))

(define (set-on-today fn)
(set! on-today fn))
(define (set-on-calculate fn)
(set! on-calculate fn))

Which is ugly and verbose, I can use a macro.

(This is Scheme, of course)