UMBC CMSC 331 Principles of Programming Languages

 

Lisp Style

Adapted from Lisp Style Tips for the Beginner by Heinrich Taube

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, understand Lisp syntax and will automatically perform this task for you. Other text editors 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.
  • 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 my_Foo (x)
        (let ((y (* x 5))
             )
            (values y x)
        )
    )
    
    looks much better (to a Lisp programmer) when formatted so:
    (defun my-foo (x)
      (let ((y (* x 5)))
        (values y x)))
    
  • Write the empty list as NIL and not ()
  • Never put a space after an open paren or before a close paren. For example, write
    (defun foo (x y z)(+ x (* y z)))
    
    rather than
    (defun foo ( x y z )( + x ( * y z )))
    
  • Cond expressions are usually broken up with each clause begionning on a new line, even if the entire cond expression could fit on a line. So instead of
      (cond ((< x 0) (foo x))((< x 10)(bar x))(t (qux x)))
    we prefer to write
    (cond ((< x 0) (foo x))
          ((< x 10)(bar x))
          (t (qux x)))
    Also, note ther use of the T as the test in the last cond clause. This is redundant in that removing it does not change the semantics. However, it's good in that its use signals to the readter that this is an "otherwise" case.

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)))
Block comments are made 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 this function is obsolete.
  (defun frob2 (list)
    (frob-aux (first list)))
  |#
comments out a function definition that is no longer used.

See Also