Next: , Previous: , Up: Top   [Contents][Index]


3 The Lisp Language

Lisp is a general-purpose language that is higher-level than C and in many ways more powerful than C. Powerful dialects of Lisp such as Common Lisp are probably much better languages for writing very large applications than is C. (Unfortunately, for many non-technical reasons C and its successor C++ have become the dominant languages for application development. These languages are both inadequate for extremely large applications, which is evidenced by the fact that newer, larger programs are becoming ever harder to write and are requiring ever more programmers despite great increases in C development environments; and by the fact that, although hardware speeds and reliability have been growing at an exponential rate, most software is still generally considered to be slow and buggy.)

The new Java language holds promise as a better general-purpose development language than C. Java has many features in common with Lisp that are not shared by C (this is not a coincidence, since Java was designed by James Gosling, a former Lisp hacker). This will be discussed more later.

For those used to C, here is a summary of the basic differences between C and Lisp:

  1. Lisp has an extremely regular syntax. Every function, expression, and control statement is written in the form
       (func arg1 arg2 ...)
    

    This is as opposed to C, which writes functions as

       func(arg1, arg2, ...)
    

    but writes expressions involving operators as (e.g.)

       arg1 + arg2
    

    and writes control statements as (e.g.)

       while (expr) { statement1; statement2; ... }
    

    Lisp equivalents of the latter two would be

       (+ arg1 arg2 ...)
    

    and

       (while expr statement1 statement2 ...)
    
  2. Lisp is a safe language. Assuming there are no bugs in the Lisp interpreter/compiler, it is impossible to write a program that “core dumps” or otherwise causes the machine to execute an illegal instruction. This is very different from C, where perhaps the most common outcome of a bug is exactly such a crash. A corollary of this is that the C operation of casting a pointer is impossible (and unnecessary) in Lisp, and that it is impossible to access memory outside the bounds of an array.
  3. Programs and data are written in the same form. The parenthesis-enclosing form described above for statements is the same form used for the most common data type in Lisp, the list. Thus, it is possible to represent any Lisp program using Lisp data types, and for one program to construct Lisp statements and then dynamically evaluate them, or cause them to execute.
  4. All objects are dynamically typed. This means that part of every object is an indication of what type it is. A Lisp program can manipulate an object without knowing what type it is, and can query an object to determine its type. This means that, correspondingly, variables and function parameters can hold objects of any type and are not normally declared as being of any particular type. This is opposed to the static typing of C, where variables can hold exactly one type of object and must be declared as such, and objects do not contain an indication of their type because it’s implicit in the variables they are stored in. It is possible in C to have a variable hold different types of objects (e.g. through the use of void * pointers or variable-argument functions), but the type information must then be passed explicitly in some other fashion, leading to additional program complexity.
  5. Allocated memory is automatically reclaimed when it is no longer in use. This operation is called garbage collection and involves looking through all variables to see what memory is being pointed to, and reclaiming any memory that is not pointed to and is thus “inaccessible” and out of use. This is as opposed to C, in which allocated memory must be explicitly reclaimed using free(). If you simply drop all pointers to memory without freeing it, it becomes “leaked” memory that still takes up space. Over a long period of time, this can cause your program to grow and grow until it runs out of memory.
  6. Lisp has built-in facilities for handling errors and exceptions. In C, when an error occurs, usually either the program exits entirely or the routine in which the error occurs returns a value indicating this. If an error occurs in a deeply-nested routine, then every routine currently called must unwind itself normally and return an error value back up to the next routine. This means that every routine must explicitly check for an error in all the routines it calls; if it does not do so, unexpected and often random behavior results. This is an extremely common source of bugs in C programs. An alternative would be to do a non-local exit using longjmp(), but that is often very dangerous because the routines that were exited past had no opportunity to clean up after themselves and may leave things in an inconsistent state, causing a crash shortly afterwards.

    Lisp provides mechanisms to make such non-local exits safe. When an error occurs, a routine simply signals that an error of a particular class has occurred, and a non-local exit takes place. Any routine can trap errors occurring in routines it calls by registering an error handler for some or all classes of errors. (If no handler is registered, a default handler, generally installed by the top-level event loop, is executed; this prints out the error and continues.) Routines can also specify cleanup code (called an unwind-protect) that will be called when control exits from a block of code, no matter how that exit occurs—i.e. even if a function deeply nested below it causes a non-local exit back to the top level.

    Note that this facility has appeared in some recent vintages of C, in particular Visual C++ and other PC compilers written for the Microsoft Win32 API.

  7. In Emacs Lisp, local variables are dynamically scoped. This means that if you declare a local variable in a particular function, and then call another function, that subfunction can “see” the local variable you declared. This is actually considered a bug in Emacs Lisp and in all other early dialects of Lisp, and was corrected in Common Lisp. (In Common Lisp, you can still declare dynamically scoped variables if you want to—they are sometimes useful—but variables by default are lexically scoped as in C.)

For those familiar with Lisp, Emacs Lisp is modelled after MacLisp, an early dialect of Lisp developed at MIT (no relation to the Macintosh computer). There is a Common Lisp compatibility package available for Emacs that provides many of the features of Common Lisp.

The Java language is derived in many ways from C, and shares a similar syntax, but has the following features in common with Lisp (and different from C):

  1. Java is a safe language, like Lisp.
  2. Java provides garbage collection, like Lisp.
  3. Java has built-in facilities for handling errors and exceptions, like Lisp.
  4. Java has a type system that combines the best advantages of both static and dynamic typing. Objects (except very simple types) are explicitly marked with their type, as in dynamic typing; but there is a hierarchy of types and functions are declared to accept only certain types, thus providing the increased compile-time error-checking of static typing.

The Java language also has some negative attributes:

  1. Java uses the edit/compile/run model of software development. This makes it hard to use interactively. For example, to use Java like bc it is necessary to write a special purpose, albeit tiny, application. In Emacs Lisp, a calculator comes built-in without any effort - one can always just type an expression in the *scratch* buffer.
  2. Java tries too hard to enforce, not merely enable, portability, making ordinary access to standard OS facilities painful. Java has an agenda. I think this is why chdir is not part of standard Java, which is inexcusable.

Unfortunately, there is no perfect language. Static typing allows a compiler to catch programmer errors and produce more efficient code, but makes programming more tedious and less fun. For the foreseeable future, an Ideal Editing and Programming Environment (and that is what SXEmacs aspires to) will be programmable in multiple languages: high level ones like Lisp for user customization and prototyping, and lower level ones for infrastructure and industrial strength applications. If I had my way, SXEmacs would be friendly towards the Python, Scheme, C++, ML, etc... communities. But there are serious technical difficulties to achieving that goal.

The word application in the previous paragraph was used intentionally. SXEmacs implements an API for programs written in Lisp that makes it a full-fledged application platform, very much like an OS inside the real OS.


Next: , Previous: , Up: Top   [Contents][Index]