April 30, 2002
Chris Rathman's posting at Lambda
the Ultimate reminded me of Peter Norvig's retrospective piece
on his 1992 book, Paradigms
of Artificial Intelligence Programming.
Norvig first looks at the state of Lisp now compared to 1997, and
concludes that the situation has become somewhat bleak. I don't get
too worried about the relative numbers of usenet postings for various
languages (part of Norvig's measure of language popularity), but he
does mention one thing that has concerned me for a while: There is no
work being done toward evolving a new ANSI standard for lisp.
I'm not sure just how concerned I should be about that. It is true,
after all, that people are adding new features to the language (Franz'
hierarchical packages, e.g.). And there are still some facilities for
which standardization might possibly be premature (such as
multiprocessing). But I worry a little bit that there won't be enough
energy in the lisp community to deal with a more formal
standardization effort when the time is right.
Lisp has warts, like any language. And eight years (the ANSI standard
came out in 1994) is a long time to live with those warts.
On the plus side, Lisp still stands out among other languages, for
many reasons. Norvig points out that other languages (particularly
Java) now offer some of the benefits that were previously unique to
Lisp. I've found, however, that when the fingers hit the keyboard,
Lisp still wins:
- First-class functions. The syntax for Java's inner classes
is so clumsy, I feel like a sucker every time I use one. And Python's
lambda is just a cruel joke.
- Interactive development. I haven't seen anything like the
industrial-strength debuggers that most lisp environments have.
- Syntax and macros. Lisp has a consistent syntax for
everything, and that syntax makes it really easy to write macros.
Either other language designers don't understand how helpful macros
are, or they quail at the prospect of implementing them for non-sexp
syntaxes. You'd think that the scripting languages, at least, would
recognize the utility of macros for creating domain specific/little
- Error handling. Other languages have exceptions. But they
don't have handler-bind
More generally, there is a huge advantage to using a language with 40
years of history (I know, I've gone on about this before): Almost
everything in the language fits together well (in what Kent Pitman
calls the language's ecology). I'm no language designer, but it's
often easy for me to look at a language and see features that could
have easily been made to work together, but don't.
(Norvig's extraction of the 52 most important lessons from PAIP is
kind of fun, too.)
P.S. lemonodor is still only half-alive. I'm settled in my new
apartment, but I still need to take care of my home high-speed
Posted by jjwiseman at April 30, 2002 02:28 PM
Re Python's lambdas: How bout blocks in Smalltalk
and Ruby. I've heard one ex-Lisper say that they
feel better than lambdas for imperative code.
Also, how important are restartable exceptions?
There's a great discussion of them (relating
real-world use histories) in _The Design and
Evolution of C++_. What do you want to use them
for? What have you used them for?
I don't know much about Smalltalk blocks; I think they are equivalent to lisp lambdas, but with extremely convenient syntax, right?
I have used CL's rather rich exception framework, though.
handler-bind lets me decide at the time the exception happens, in the dynamic context that it happened in, how to handle the exception. I've used it to grab extra information about the call stack at the time the exception occurred.
Restarts I have found to be useful mostly for development and debugging. They're used in many lisp implementations, for example, to handle the case where you've mistyped the filename of the lisp file you want to load, or you typoed a variable name. Instead of having to stop, correct the error, reload and restart, you can often indicate that you want to skip loading the file, load another file, use the value of another variable just this once, or use the value of another variable from now on. It's reallly convenient, and all implemented with restarts.
I use restarts quite a bit in the form of continuable errors: instead of just raising an error, I can raise an error but also offer the choice of ignoring it in a sane way. For example, in the RAP robot task execution system I worked on, when an error occured while executing a task you were given the option of continuing as if the task had failed. And this didn't have to be a decision that the user made while interacting with a debugger through a prompt or a dialog box; code *above* the task execution system can use handler-bind to call the 'continue' restart if it wanted, without the user being involved, and importantly, without the task execution system having to know anything about it.
I have only skimmed _The Design and Evolution of C++_, sounds like it might be worth reading more closely.