Lisp Style Tips for the Beginner

Heinrich Taube
Zentrum för Kunst und Medientechnologie
Ritterstraûe 42 76137 Karlsruhe Germany
Email: hkt@zkm.de

This document is an informal compendium of tips, conventions and rules for writing efficient, legible Lisp code.

Evaluation

· Never use eval.  The evaluation process is already built into Lisp so there is no reason to call the evaluator yourself.  If you need eval to implement something then stop and reconsider the problem.  You are almost certainly thinking about it incorrectly. 

· Beware of macros.  They are a very important feature of Lisp, but a poorly implemented macro can cause problems that are difficult for a beginner to debug.   Avoid writing macros until you really understand how the Lisp reader and evaluator work.  Never write a macro to "make things more efficient".

Functions

· Don't write C or Pascal programs as Lisp functions.  Lisp programming style recommends that you try to keep function definitions as small as possible.  Each function should accomplish only one task.  By implementing lots of small functions instead of a few large ones you increase the clarity, modularity and reusability of your code.

· Before you implement a function, make sure that Lisp doesn't already provide it.   Lisp has literally hundreds of functions; acquaint yourself with relevant sections in CLtL2 before you start a programming task. 

· If you implement a function that returns more than one result, uses values rather than returning the results in a list.   If your function returns no results (like a void in C),  use (values) to return zero values.

· Don't add declarations to your code until you are a seasoned Lisp programmer and you are absolutely certain how the variables you declare are used.

· Don't write huge case or cond statements.  Consider other strategies such as data-driven programming or hash-tables instead.

Symbols and Variables

· Common Lisp is case insensitive but most Lisp programmers prefer to use only lower case letters.  Use the hyphen to form multi-word symbols, as in multi-word-symbol.  Lisp tends to be verbose, get used to typing long symbol names!

· Indicate global variables by **, as in *standard-output*. 

· Indicate global constants by ++, as in +default-number+ .

· Use setf rather than setq to set variables.  setf stands for SET Field.   setf is a more general form of setq that sets almost anything you can "point to", or reference, in Lisp.  This includes variables, array locations, list elements, hash table entries, structure fields, and object slots.

Lists

· Use first, rest, second and so on rather than their car, cdr, cadr equivalents.  

· Beware of destructive list operations.  These are efficient but can be the source of obscure bugs in your program.

· Remember that length must chase pointers to find the end of a list.  If you refer to the length of something more than once, save the value in a variable rather than calling length multiple times.

· Remember that append copies its arguments.  Avoid using append inside a loop to add elements to the back of a list.   Use the collect clause in loop, or push elements onto a list and then nreverse the list to return the original ordering.

· Remember that copy only copies the outer-most level of a list.  Use copy-tree to copy all levels of a list.

Conditionals

· Use when and unless if there is no else clause and you do not need to use the value of the conditional expression.  Otherwise, use if when the conditional expression is simple, otherwise use cond.  Always supply a final t clause for cond.

· Lisp programmers often use the functions and and or to implement simple conditional evaluation.  For example,  

	(and x (setf y t))

is equivalent to

	(when x		
	  (setf y t)) 

and 

	(or x (setf y t))

is equivalent to

	(unless x
	  (setf y t))

Iteration

Avoid using do, which is almost impossible to read.  dotimes and dolist are fine for simple iteration.  If you need to save, store or modify results of an iteration, then loop is probably the most legible and efficient construct to use.  Some Lisp programmers avoid loop because it does not "look like lisp".  Loop is nevertheless a convenient and very powerful facility.  After 10 minutes of working with do, most Lisp programmers are glad to have loop around!

Comments

Provide them.  Document overall functionality and explain sections of code that are a bit tricky (you will forget how you implemented something in about 2 week's time.)  Lisp provides two different types of comments.  The semi-colon ; introduces a line-oriented comment.  Lisp ignores whatever follows a semi-colon until the end of the line that the semi-colon is on.  The semi-colon does not have to be the first character in the line.  Here are two examples:

	; this is a comment
	(abs x) ; need absolute value here!

By convention, Lisp programmers distinguish between one, two or three semi-colon comments.  A comment introduced by a single semi-colon explains code on the same line as the comment.  Two semi-colons mean that the comment is a description about the state of the program at that point, or an explanation of the next section of code.  A two semi-colon comment should start at the same indentation column as the code it documents.  A three semi-colon comment provides a global explanation and always starts at the left margin.  Here is an example containing all three types:

;;;  the next 20 functions do various sorts of frobbing 

(defun frob1 (num)
  ;; return double frob of num
  (let ((tmp (random num)))      ; breaks if 0, fix!
    (double-frob tmp num :with-good-luck t)))

Lisp also allows block commenting by placing text within #| and |#.   Lisp ignores anything between the balancing delimiters no matter how may lines of text are included.  #| |# pairs are often used to comment out large sections of program code in a file or function.  For example:

#|
i think frob2 is obsolete, but i wont delete it yet.
(defun frob2 (list)
  (frob-aux (first list)))
|#

comments out a function definition that is no longer used.

Formatting and Indentation

Poorly formatted Lisp code is difficult to read.  The most important prerequisite for legible Lisp code is a simple, consistent style of indentation.  Some text editors, such as Emacs or Fred, understand Lisp syntax and will automatically perform this task for you.  Other text editors, such as NeXTStep's Edit.app, have no understanding of Lisp beyond  parentheses matching.  Even if you use a text editor that cannot perform Lisp indentation, you should take the time to format your code properly.  Here are a few simple rules to follow:

· If your editor supports multiple fonts, always display Lisp code in a fixed-width family like Courier or Ohlfs.

· Avoid writing lines longer than 70 characters.  Don't assume your reader can shape a window as large as you can,  and wrap-around text is almost impossible to read on hard-copy.

· Indent forms in the body of a defun, defmacro or let clause two spaces to the right of the column in which the clause starts.  In the following example, both forms in the defun are indented two columns.  The forms in the body of the let are indented two columns to the right of where the let starts:

(defun foo (a b &aux c)
  (setf c (* a b))
  (let ((d 1)
        (e (if (zerop b) t nil)))
    (check-compatibility d c)
    (foo-aux a b c d e))) 

· If the arguments to a function occupy more than a single line, indent subsequent lines to the same column position as the first argument.  In the following example the indentation clearly shows that there are three arguments to compute-closure.

(setf s (compute-closure this-function
                         (list other-function my-method)
                         56))

· Don't use tab to indent and don't close parenthesis on a single line.  C style formatting looks silly in Lisp.  The example code:

(defun myFoo (x)
		(let ((y (* x 5))
			)
			(values y x)
		)
)

looks much better (to a Lisp programmer) when formatted as:

(defun my-foo (x)
  (let ((y (* x 5)))
    (values y x)))