[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Using Macros in Lisp

Luke Hohmann writes:
 > I am trying to use macros in a very large program to 
 >   a. reduce the size of the source code,
 >   b. make the source code easier to read,
 >   c. Be a really cool lisp programmer.

My impression is that, according to conventional wisdom, macros
are to be avoided wherever function calls will do, because they:

   a. are hard to write,
   b. make the source code harder to read,
   c. make the program *much* harder to debug.

Conventional wisdom or not, I know (a) and (c) are true!  I do
use a few macros, however, and you have to understand macros to
write "defsetf" forms.

I do use them, however.  Here are two that I use:

; INSIST  written by Christopher Reisbeck, *slightly* modified
; by Tom McDougal.
; A nice little anti-bugging tool for argument-checking.
; Syntax:
;    (insist <where> -tests- )
; Example of use:
;    (defun invert (x)
;       (insist foo (numberp x) (not (zerop x)))
;       (/ x))
; If any of the -tests- fails at run-time, an error message
; is printed identifying the place (<where>) and the test
; that failed.  E.g.
; > (foo 0)
; Error: (not (zerop x)) failed in foo.
; INSIST expands into a large COND.

(defmacro insist (where &rest tests)
   `(cond ,@(make-insist-forms where tests)))

; make-insist-forms returns a list of COND clauses.  I recommend
; trying it out independently to get a feel for what is going on.
; E.g.  (make-insist-forms 'foo '((numberp x) (not (zerop x))))

(defun make-insist-forms (where tests)
  (cond ((null tests) '())
        (t (cons `((not ,(car tests))
                   (error "~s failed in ~s"
                          ,(car tests) ,where))
                 (make-insist-forms where (cdr tests))))))


; Example 2: Most of us around here are former T hackers, so we
; like the following:

; ITERATE from T.  The version I saw was written by Jim Firby, but
; Slade probably did it first.
; How many times do you want to march down a list until you
; find something?  How many times do you find yourself writing
; a little auxilliary recursive function?
; Syntax:
;    (iterate <name> <args-w/-initial-bindings>
;       -body-)
; Example:
;    (defun fact (x)
;      (iterate fact-aux ((a x) (result 1))
;         (if (zerop a)
;           result
;           (fact-aux (1- a) (* result a)))))
; This macro expands into a LABELS construct.

(defmacro iterate (name bindings &body body)
  `(labels ((name ,(mapcar #'car bindings)
     ,(cons name (mapcar #'cadr bindings))))

; So, FACT expands into:
; (defun fact (x)
;   (labels ((fact-aux (a result)
; 	       (if (zerop a)
;                result
;                (fact-aux (1- a) (* result a)))))
;     (fact-aux x 1)))

; Hope this helps!

   --Tom  (mcdougal@cs.uchicago.edu)

	2025, Edgar Spindizzy realizes that
	his time machine works.