A Comparison of Python and LISP

This brief summarizes a number of similarities and differences between Python and LISP, as pertains to one particular situation: data analysis and equipment control in a neuroscience lab. We must decide whether to stay with our current system, based on Mac Common Lisp, or to convert the bulk of our systems to a different language. The possible languages were quickly narrowed down by some important criteria, such as interactivity and MacOS compatibility. The only serious contender left was Python.

The relative merits of the two languages are compared in 11 categories. In an attempt to make a more quantitative comparison, each language is assigned a score from 0 to 10 in each category. These scores are then combined, with appropriate weighting factors, to produce a total score. Each score was assigned as independently as possible, before weighting factors were decided; and weighting factors were chosen without examining the scores. In this way, the final scores are made as objective as possible.

Below, "LISP" refers specifically to Macintosh Common Lisp, except where a more general meaning is indicated by context. Similarly, the Python implementation in question in Mac Python (1.5.2b1). Other implementations of either language may differ in some respects.


Maturity

LISP was first developed over 40 years ago, and it's had a long time to mature. There is an ANSI standard for the language, and many LISP compilers have grown quite efficient and bug-free.

Python's development started in 1990, less than a decade ago. However, because it is an open-source project, it developed very quickly. Bugs have been very rare, and have always been found and fixed rapidly after a new release. In addition, because all changes go through a central organization (originally CWI in Amsterdam, and now CNRI in Reston, VA), the language has been kept consistent and runs identically on all platforms.

LISP scores 10/10 for maturity. Python is much younger, but because of the quality and consistency of the implementations, it deserves 7/10. (In comparison, Java would currently rank only about 3/10, despite the huge user base, because of many inefficient and buggy implementations.)

Syntax

Python uses an algebraic syntax, that is, the same syntax as used in common algebra. Binary operators appear between their operands, grouped by precedence rules or (optional) paretheses, and functions are invoked as f(x). LISP uses a prefix notation, where operators or function names preceed their arguments. This is illustrated by a simple example:

English"y equals m times x plus b"
Mathematicsy = m x + b
Pythony = m*x + b
LISP(SETQ y (+ (* m x) b))

While LISP syntax is very easy for a computer to parse, it does not follow the standard rules used by English, mathematics, and virtually all modern (i.e. developed within the last 30 years) programming languages. As a result, LISP code is unreadable to someone not experienced in the language, and learning the required skill takes considerable time. Python, in contrast, is mostly readable even to someone who has never seen Python code before.

If LISP were as consistent in its syntax as it first appears, the problem would be more approachable. Unfortunately, this is not the case; there are over two dozen predefined "read-macros" which change the syntax that is actually used, and more such can be defined by the user. So even after one grasps the notion of prefix notation, one must also learn the common read macros and what they do. Page 2 of the ANSI Common Lisp book (Graham, 1996) gives this example:

	(defun addn (n)
	  #'(lambda (x)
	      (+ x n)))
which is not understandable until you learn about the #' macro on page 25. In Python, this same function would be
	def addn(n):
		return lambda x,inc=n: x+inc
Both versions return a lambda function, which is a fairly advanced concept to a beginning (or procedural) programmer. But the Python version does not use any special syntax.

If this example seems obscure, consider the very simple case of binding a variable to a new list. In LISP, this is

	(SETQ A '(2 4 6 8))
which is fairly readable -- but what's that apostrophe doing there? It's another read-macro, which expands to a special operator (QUOTE), which keeps LISP from trying to apply "2" as a function. (SETQ is also a special operator that violates standard evaluation rules.) In Python, you'd write
	A = [2, 4, 6, 8]
which is even more readable, and does not involve any special operators or pre-processing.

In fact, Python does not have anything like read-macros, and doesn't need them because of its algebraic syntax. As a result, it can be argued that Python syntax is more consistent than LISP; everything is either a (unary or binary) operator or a function, with no special cases or exceptions.

Because of LISP's unconventional and (in practice) inconsistent syntax makes it so difficult to learn, I'm giving it 2/10 for syntax. Of all the languages I've used (including C, C++, Pascal, and FORTRAN), Python's syntax is both easiest to learn and most elegant to use, so it gets 10/10. (By comparison, C would rate about a 5.) Note, however, that syntax is largely a matter of taste, and a steep learning curve may be more or less important depending on how the language is to be used. So this score is quite debatable.

Expressiveness

The expressiveness of a language is hard to define; it is the collection of features which make a language easy to use, self-documenting, elegant. Among these features: Both languages are very expressive. Python doesn't have as many numeric types, so we'll take one point off (9/10) for that. LISP has basically all the same functionality as Python, but in several cases -- list manipulation, exception handling, dictionaries -- it is less well-developed, resulting in reduced clarity, robustness, or efficiency. 8/10 for LISP's expressiveness.

Functionality

When LISP was developed in 1958, it could do things no other language could do. It innovated the notion of treating functions as objects that could be assigned to variables, returned from other functions, etc. This functional programming paradigm is very powerful and allows for some very elegant solutions -- for example, one can write a generic sort function that takes, as one of its parameters, a function to do the comparison between any two elements.

Many modern languages (e.g., C++ and Java) still lack this capability. But Python is not one of those; functions are objects which can indeed be passed around, used as parameters, returned as values, operated upon, etc. It is one of the vital features that make both LISP and Python such powerful languages.

Another related capability is that of constructing and compiling entirely new functions at runtime, possibly from strings or other primitives. Both LISP and Python have his ability as well.

Both languages score 10/10 in functionality.

Developer Activity

A strong and busy developer community ensures that language implementations continue to evolve, that bugs will be quickly found and fixed, and that help is available when programming difficulties arise. It's very difficult to gauge the number of active developers. Clearly, LISP was at one time the dominant high-level language, but many LISP programmers have since switched to other languages. A variant of LISP known as Scheme is still common on the Unix platform; a particular Scheme implementation called Guile forms the extension language for many GNU software apps. On the Mac platform, LISP has not fared so well; the only serious LISP implementation is Mac Common Lisp (MCL), which was abandoned by Apple in the early 90's and licensed to Digitool. Digitool claims to release new versions of MCL "every four to six months", but in fact has not shipped a new release or posted any news since 1997. Note, however, that messages from Digitool employees to the MCL mailing lists indicates that they're still working on the product.

Python's developer community has been steadily growing, and now appears to number in the thousands. Traffic on the Python mailing list shows a roughly exponential rise since 1991, with no signs of levelling off. A number of organizations use Python, including NASA, IBM, SGI, LANL, and LLNL. Of course it is difficult to extrapolate to actual numbers, but it is clear that the Python developer community is growing.

LISP books were common in the 80s and early 90s. A quick search at Amazon.Com found only one book on ANSI Common Lisp published after 1995 (plus a few books on "Autolisp", the extension language used with the Autocad application). There have been three Python books in this period -- but those may be all the Python books which exist.

The main Python newsgroup (comp.lang.python) has outnumbered the number of messages in the comp.lang.lisp and comp.lang.lisp.mcl newsgroups combined by a factor of 3 or so during an arbitrary survey period (the latter half of January 1999). Of course, this could be because LISP users, with a wider variety of reference books on hand, don't need to post questions to the newsgroups; there is also an MCL mailing list which gets several messages a day.

Overall, it appears that the LISP community is large but shrinking, especially on the MacOS platform. On other platforms it is evolving into derivative languages (such as Scheme) and specialized extension languages, such as that of Autocad and emacs. The Python community is comparatively small but growing rapidly. Both communities are small compared to C++, which is very mature, or Java, which is backed by a great deal of money. Thinking about both current size and direction of growth, LISP rates a 5/10, and Python a 4/10.

Open Source

Open-source free software has a number of advantages over commercial software. It tends to be more stable, more portable, and more efficient than commercial software, because thousands of developers around the world contribute improvements and bug fixes. Python has benefitted enormously from this effect, resulting in a very mature product in under a decade.

Unlike the last category, this one is easy to score: Python is open-source (10/10), LISP (at least MCL) is not (0/10). Note, however, that we'll weight this category less than others in the final scoring.

Available Modules

Both LISP and Python have a restricted set of built-in functionality; all other functionality is contributed through library "modules". The quantity and quality of available modules may make a big difference in the amount of code the enduser has to write and maintain.

The standard Python distribution includes over 150 modules for diverse tasks such as random number generation, networking, parsing HTML or XML, encryption and compression, matrix manipulation, color handling, parsing, and regular expressions. The Mac distribution contains 100 more for handling things like Apple Events, GUI elements, manipulating the Finder, Internet Config, printing, etc. In addition, a large number of contributed modules are available for such needs as numerical algebra, interfacing to OpenGL, plotting, relational database handling, generating PDF or Postscript, and computational chemistry. Because of Python's multiple namespace feature, these can be used in any combination without fear of conflicts.

LISP has a large library of built-in functions, but I could not find a central repository of library modules, even at an index of Lisp Resources or via Yahoo. The CMU Common Lisp Repository contains some Lisp code for "benchmarking, research, education, and fun", but nothing comparable to Python's extensive library of modules which are actually useful. The only large library of LISP code I could find was a set of Artificial Intelligence Packages (also at CMU).

For its extensive set of useful and powerful modules, Python earns 10/10. LISP seems to be lacking this platform of developed, plug-in code except in the field of AI. On the other hand, particular implementations like MCL do contain their own libraries for accessing the toolbox and other common operations. LISP scores 4/10 in this category.

Note: I've been told that good libraries of LISP code (besides AI) do exist if you know where to look. So this score may need to be changed soon.

Extensibility/Embeddability

A high-level language is especially valuable when it can be easily extended with high-performance code written in C or C++. In some cases, it is also valuable to go the other way, i.e., to embed the high-level language into a C/C++ application.

Python was designed to support both methods of integration. Calling C code from Python is very easy via the Simple Wrapper Interface Generator. Embedding Python in other code is equally easy. In addition, a new integration approach called JPython seamlessly combines Java and Python. (These issues are further discussed in an essay by Guido van Rossum, and documented in Extending and Embedding the Python Interpreter.) 10/10 for Python in this category.

LISP can make use of code written in C. Some variants (like elisp) have been used as embedded languages as well, but ANSI Common Lisp proper is not easily embedded -- or at least, not on the Mac platform. However, since this is less important than extensibility, LISP loses only 2 points. Since Java is likely to be increasingly important in the future, 1 additional point is lost for no integration (in either direction) with Java.

Documentation

MCL comes with detailed manuals, and a large number of books are available for ANSI Common Lisp in general. While somewhat scattered and not easily searchable, this represents a significant amount of well-written documentation, earning 7/10.

The Python Documentation is virtually all online in HTML form. It is well-indexed, cross-linked, and complete, even including a very good tutorial; it is searchable, both via the web and via a Sherlock plug-in. There is also an O'Reilly book on Python programming. But that's about it -- Python lacks the quantity of books available on Lisp programming, leaving one's bookshelf a bit bare. Moreover, some of the modules (especially contributed modules) are documented less well than others. So Python earns 8/10 in this category -- slightly higher than LISP mainly because searchable, cross-indexed documentation is considered more useful than books.

MacOS Integration

For our purposes, it is important that the language we use get along well with the MacOS environment. For example, we want to be able to generate pictures as PICT structures, rather than pixel maps; we should be able to print, etc. Both MCL and MacPython seem to have these common functions well in hand. However, both currently lack support for the latest MacOS features, such as the Navigation Manager and the Appearance Manager.

Another aspect of MacOS integration is the integrated development environment (IDE). MCL's IDE is outstanding; it has powerful editing features, good interactivity, window control, good search capability, etc. It is only lacking in a few newer features such as drag-and-drop and syntax highlighting. The MacPython IDE is less developed, but improving rapidly. It has many of the same features as MCL's, including an object inspector and the interactive window, but is somewhat lacking in multi-file search and a few other areas. However, since the IDE is itself written in Python and open-source, it is improving rapidly.

Finally, each environment must be examined with an eye towards the future. MacOS X Server is coming next month, and MacOS X should be released before the end of 1999. MacPython has tested as "carbon-ready" and should run immediately under MacOS X without needing the "blue box" emulation layer. In addition, since Python also runs under many flavors of Unix, it is reasonable to expect that a fully MacOS X native version of Python will appear shortly after that OS is released.

MCL appears much less poised to transition to future MacOS systems. At its last release, in December 1997, MacOS 8.1 was the latest version of the OS. Though it runs well in 8.5 -- indicating properly-written software -- it naturally does not use the latest features, and is probably not Carbon-compliant. Without an update, it seems likely that MCL will run under MacOS X only within a blue box. However, recent messages to the MCL mailing list indicate that Digitool is indeed thinking about MacOS X and will probably produce a compatible version.

Python's Mac support is imperfect, but rather good, and it seems quite ready for MacOS upgrades; it scores 8/10. MCL's Mac support was very good in 1997, but is getting a bit dated, and I still question whether it will keep up with the changes to the OS -- so it gets 7/10.

Current Code

This category is for our particular situation -- we have a lot of code written in LISP already, which will need to be replaced if we switch to Python. At this point, we have only one major app in Python, which would need to be rewritten if we stay with LISP. To reflect these inertial costs, Python scores 1/10 and LISP scores 9/10 in this category.


Each of the raw scores attained above is included in the table below. They are then adjusted by a weighting factor. This reflects the relative importance of the categories; for example, we want a language which is very expressive; but whether the language is open-source or commercial is of only minor importance.

raw scores * weight = adjusted scores
PythonLISPPythonLISP
Maturity710* 53550
Syntax102* 88016
Expressiveness98* 109080
Functionality1010* 55050
Dev. Activity45* 83240
Open Source100* 3300
Available Modules104* 77028
Extensibility107* 88056
Documentation87* 64842
Mac Integration87* 43228
Current Code110* 5945
Total8769 557435

These results suggest that switching to Python is probably the correct choice for our situation. Python equals or surpasses LISP in 8 out of 11 categories -- or 8 out of 10 if we ignore our current code base. I can see only two plausible ways this conclusion could be avoided:

  1. One could argue that LISP syntax, despite being unconventional and therefore difficult for beginners to learn and use, is somehow superior to algebraic syntax. (Indeed, LISP programmers often argue exactly this.) Or one could argue that syntax does not matter much, and reduce the weight of that factor, closing the distance between LISP and Python somewhat.

  2. One could increase the weight of the "current code" category; i.e., the cost of rewriting existing LISP code weighed against other factors. This may be justified in our situation by the huge amount of LISP code in use; and we must retain the ability to re-run old analyses.
Both of these obviously depend on usage; for an environment where code is only touched by experienced programmers, LISP syntax may not be an issue; but in an environment where non-programmers must be able to read and write basic code, that argument becomes weak. And the latter notion -- i.e., increasing the weight of the current code -- makes sense mainly in the short term.


Recommendation

Based on these considerations, I recommend the following strategy:
  1. Continue to develop the Python environment: improve the IDE as needed for our purposes, develop extensions to the back-end C libraries already in use, find a good plotting/graphing package, and see that Python is installed and kept current on all machines.

  2. Gradually replace existing parts of our current system with Python equivalents, especially for those stand-alone apps such as Group and Cell. (This process is already underway with the new "Cell Finder" program.)

    At the same time, see whether Python and MCL can be integrated via the Inter-Language Unification system (ILU). If so, this will allow us to call existing LISP code from the new Python programs, enabling a smooth, piece-by-piece transition.

  3. As maintaining dual analysis systems is onerous, at some point it will be worthwhile to discontinue use of the MCL code. This can be used as an opportunity to consider the features and design of the new system, and do a careful redesign to make the second-generation system even better than the first.

pythonvslisp.html . . . . . . . . . . 1/30/99 . . . . . . . . Joseph J. Strout (joe@strout.net)