Correct way to incorporate a docstring in a def* macro?

152 views Asked by At

I am working my way through Practical Common Lisp. I got to the example where you define a deftest macro that works like defun with some added functionality. This got me thinking that it would be nice to be able to add a docstring. I have found that both of the following work, but is one of them more correct? Is there a "right" way to achieve the optional-docstring-like behaviour of defun?

(defmacro deftest (name parameters &body body)
  (let ((docstring ""))
    (when (stringp (car body)) (setf docstring (car body) body (cdr body)))
    `(defun ,name ,parameters
       ,docstring
       (let ((*test-name* (append *test-name* (list ',name))))
         ,@body))))

(defmacro deftest (name parameters &optional docstring &body body)
  (when (not (stringp docstring)) (setf docstring ""))
  `(defun ,name ,parameters
     ,docstring
     (let ((*test-name* (append *test-name* (list ',name))))
       ,@body)))
1

There are 1 answers

6
AudioBubble On BEST ANSWER

In general you probably want to parse out both a possible docstring and any declarations from the body of the function, so you can put the declarations where they belong. I use something like this:

(defun parse-body (body)
    (multiple-value-bind (docstring decls/forms)
        (if (stringp (first body))
            (values (first body) (rest body))
          (values nil body))
      (loop for remainder on decls/forms
            while (and (not (null remainder))
                       (consp (first remainder))
                       (eql (car (first remainder)) 'declare))
            collect (first remainder) into decls
            finally (return (values docstring decls remainder)))))

And then your deftest would be

(defmacro deftest (name parameters &body body)
  (multiple-value-bind (docstring decls forms) (parse-body body)
    `(defun ,name ,parameters
       ,@(if docstring (list docstring) '())
       ,@decls
       (let ((*test-name* (append *test-name* (list ',name))))
         ,@forms))))

I wish I could say that I have a standard parse-body function, but I don't: I just write a new one each time.