Why Lisp?
Lisp is a big, complicated language. Why
use it instead of something smaller and
simpler like C?
The answer lies in Greenspun's Tenth
Rule of Programming: ``Any
sufficiently-complicated C or
Fortran program contains
an ad-hoc, informally-specified bug-ridden
slow implementation of half of Common
Lisp.'' (To answer the obvious question,
nobody knows what the first nine rules
are.) (There was a discussion of this law on the Squeak
mailing list around 1998-04-15.)
What are these features? Well, here are
some of them:
- Lists provide a simple, uniform way to
sling around multiple values, something
that is difficult to do in low-level
languages without arbitrary restrictions.
They also provide a convenient and easy
way to build more complex data structures.
- Garbage-collection provides much safer,
more accurate memory management. It's also
sometimes faster, and it promotes fewer
dependencies between modules. The absence of
garbage-collection often drives programmers
in C to use ad-hoc, informally-specified,
usually bug-ridden, and almost always slow
techniques to manage memory. Two of the
most common workarounds are copying the
entire contents of big data structures,
rather than copying pointers to them, so
that no instance of the big data structure
has more than one ``owner'' --- so when the
``owner'' goes away, the big data structure
can too --- and reference-counting, which
makes the copying of pointers very slow,
and which breaks in the case of cyclic
data structures. Other common results are
premature frees (which cause crashes) and
memory leaks (which cause pain).
On the minus side, garbage-collection
usually uses
a lot more memory than explicit memory
management.
- atoms let you give a name to options.
Rather than doing slow string compares all
over the place, you just intern something
as an atom, and then compare its value,
which is fast. (The C equivalent is ``enum'',
which has the disadvantage that
all of the alternatives have to be declared
in one place.)
- macros let you extend the language in
arbitrarily powerful ways. This means you
can do a lot of things at compile time that
you have to do at run time in C, C++, etc.
- run-time compilation lets you do many
things much more efficiently than you can
in C, at least without a lot of work. If
you write an interpreter for a toy language
as part of a Lisp application, your
toy language can actually run as fast as
machine code, because it's compiled to
machine code. But if you do the same thing
in C, compiling to machine code is a large
undertaking, so you just write an
interpreter.
This is easy in Lisp because, in Lisp, it's easy to manipulate
fragments of code. You can pass them around, combine them, and modify
them.
- it has a sophisticated error-handling
system that I don't know much about. (C++
corrects this deficiency of C.)
- it bounds-checks all array accesses, so
you don't get mysterious crashes. Further,
any particular implementation has likely
put quite a lot of work into making
the bounds-checking faster.
- all data is dynamically typed. This has
the major disadvantage that lots of things
need the type to be dynamically checked
at runtime, but provides a lot of
flexibility, flexibility that you end up needing anyway in large systems.
This doesn't hinder strong typing (which is necessary to prevent
the accidental or intentional circumvention or the bounds-checking
system.)
- dynamic method dispatch is built in, along
with lots of other OO features that C --- or even C++ ---
programmers can only dream of, but that
I know almost nothing about.
- Lisp lets you easily include fragments
of code as arguments to functions. This
greatly simplifies things like GUI
programming.
- Lisp lets you pass around closures, which
are pieces of code along with some associated
data, quite easily. This is a minor headache
in C and C++.
What do you think? Mail me.