Saturday, December 10, 2005

Lisp and Elegance

Disclaimer: I am new to Lisp, and the idioms and syntax are still somewhat alien to me, so there.

I am in Chapter 9 of Practical Common Lisp, in which Peter Seibel develops a unit testing framework to build on the concept of macros. By the end of the chapter, we end up with just 26 lines of code that is quite powerful (albeit slightly arcane) and elegant (apologies for the horrendous formatting):

(defvar *test-name* nil)

(defmacro deftest (name parameters &body body)
  "Define a test function. Within a test function we can call
   other test functions or use 'check' to run individual test
   cases."
  `(defun ,name ,parameters
    (let ((*test-name* (append *test-name* (list ',name))))
      ,@body)))

(defmacro check (&body forms)
  "Run each expression in 'forms' as a test case."
  `(combine-results
    ,@(loop for f in forms collect `(report-result ,f ',f))))

(defmacro combine-results (&body forms)
  "Combine the results (as booleans) of evaluating 'forms' in order."
  (with-gensyms (result)
    `(let ((,result t))
      ,@(loop for f in forms collect `(unless ,f (setf ,result nil)))
      ,result)))

(defun report-result (result form)
  "Report the results of a single test case. Called by 'check'."
  (format t "~:[FAIL~;pass~] ... ~a: ~a~%" result *test-name* form)
  result)

All well and good, and I appreciate the way in which we ended up with this code, starting from a simple definition of the problem, recognising repeated patterns and refactoring them via macros, but my grouse is this: from a maintainability perspective, the final code, while very powerful, and well-commented to boot, still contains none of the thought processes that led to it. May be it's alright if the same person who wrote the code is also doing the maintaining, but IMHO, unless they are documented somewhere, another person will definitely have trouble grokking things.