Re: Avoiding eval

From: Thomas A. Russ (tar_at_sevak.isi.edu)
Date: 02/23/04


Date: 23 Feb 2004 10:34:52 -0800

An Metet <Use-Author-Address-Header@[127.1]> writes:

>
> NOTE: This message was sent thru a mail2news gateway.
> No effort was made to verify the identity of the sender.
> --------------------------------------------------------
>
> >From: tar@sevak.isi.edu (Thomas A. Russ)
>
> >> I had tried to use APPLY on my macro but it failed
> >>
> >> (dolist (args arg-list "done") (apply #'item-classm args));fails
> >> But entering the arguments directly works.
> >
> >How did it fail?
>
> The question suggests that you think it should work,
> I was quite dissapointed when it didn't.

Actually, I took you at your word and didn't expect it to work, but I
thought that the way it failed might prove to be instructive. At the
very least, telling us what you find out can help focus our attention on
potential problems. Since we're just volunteers, we (or at least I)
don't have a lot of time to spend on this list.

> It Fails just like this
>
> CL-USER 6 > (item-classm "testname")
> #<STANDARD-CLASS |testname| 2067DD3C>
>
> CL-USER 7 > (macroexpand-1 '(item-classm "testname"))
> (DEFCLASS |testname| (COMMON |testname|) NIL)
> T

OK, this looks rather suspicious, and is one reason to use macroexpand
to make sure your macro really is producing the code you want it to
produce. If you look at the expansion, you will see that you are
defining a class which has itself as a superclass. That isn't allowed.

So, you either need to generate a different classname (which is what I
assume below) or you need to recognize this case as building the base
class and exclude the category from the superclasses if there are no
options.

>
> CL-USER 8 > (dolist (args arg-list "done") (apply #'item-classm args))
>
> Error: Call ((LAMBDA (#:%%MACROARG%% #:&ENVIRONMENT525 &AUX
> (#:&WHOLE526 #:%%MACROARG%%) (#:\(CATEGORY\ ...\)527 #)
> (#:CHECK-LAMBDA-LIST-TOP-LEVEL531 #) (#:CATEGORY #) (#:KEYS528 #)
> (#:SUPPLIED-P529 #) (#:OPT-A #) (#:SUPPLIED-P530 #) (#:OPT-B #))
> (DECLARE (SPECIAL:SOURCE #) (LAMBDA-LIST CATEGORY &KEY OPT-A OPT-B))
> (BLOCK #:ITEM-CLASSM (LET # #))) "mollusc") has the wrong number of arguments.
> 1 (abort) Return to level 0.
> 2 Return to top loop level 0.
>
> Type :b for backtrace, :c <option number> to proceed, or :? for other options
>
> CL-USER 9 : 1 >
>
>
> The macro ITEM-CLASSM has optional keyword arguments and it apears to me
> as if they are being treat as "REQUIRED" when using the macro in this way.

First off, a macro is not a function, so you can't APPLY it. But
instead of telling you that, your lisp is taking the macroexpansion
function and trying to apply that instead. It is the wrong thing to do.

The fundamental rule is: You Cannot APPLY or FUNCALL macros.

> Below is the ITEM-CLASSM macro and code for the argument lists repeated

Looking briefly at your macro, I notice that the result of the expansion
is basically the same with the only change being in the construction of
the list of superclasses and the name of the class. That being the
case, I would structure things a little more simply, with auxiliary
functions. One that computes the superclasses of the class you are
trying to define and another for the class name . That also has the
advantage that you can test more parts of the code separately from the
command line and make sure they do what you want them to do.

For example:

(defun compute-superclasses (category opt-a opt-b)
  ;; Note that we build this list up backwards using PUSH
  (let ((supers nil))
    (when opt-b (push opt-b supers))
    (when opt-a (push opt-1 supers))
    (push category supers)
    ;; Alternately: (when (or opt-b opt-a) (push category supers))
    ;; Use the alternative if you choose the other option to fixing
    ;; the circular superclass problem.
    (push 'common supers)
    supers))

(defun compute-class-name (category opt-a opt-b)
  ;; Left as exercise for the reader. Just extract the code
  ;; from your current macro.
  ;; NOTE: MAKE SURE YOU MAKE A NAME DIFFERENT FROM THE category VALUE
  )

Then your macro becomes a lost simpler:

(defmacro Item-classm (category &key opt-a opt-b)
  `(defclass ,(compute-class-name category opt-a opt-b)
             ,(compute-superclasses category opt-a opt-b)))

This also assumes that you have replaced the lists with symbols. That
will just simplify your programming:

(defparameter *category-list*
   '(mollusc fish insect mammal bird arachnid reptile amphibian))
(defparameter *opt-a-list*
  '(walking flying swimming crawling))
(defparameter *opt-b-list*
   '(wild domesticated extinct))

Then you invoke your macro like:

(item-classm insect :opt-a flying :opt-b wild)

> (defparameter *category-list*
> '( "mollusc" "fish" "insect" "mammal" "bird" "fish" "arachnid" "reptile" "amphibian"))
> (defparameter *opt-a-list*
> '( "walking" "flying" "swimming" "crawling"))
> (defparameter *opt-b-list*
> '("wild" "domesticated" "extinct"))
> (pprint (setq arg-list
> (concatenate 'list
> (mapcar #'list *category-list*)
> (mapcan #'(lambda(X)
> (mapcar #'(lambda (Y)
> (list X :opt-a Y ))
> *opt-a-list*))
> *category-list*)
> (mapcan #'(lambda(X)
> (mapcar #'(lambda (Z)
> (list X :opt-b Z ))
> *opt-b-list*))
> *category-list*)
> (mapcaN #'(lambda(X)
> (mapcan #'(lambda(Y)
> (mapcar #'(lambda (Z)
> (list X :opt-a Y :opt-b Z ))
> *opt-b-list*))
> *opt-a-list*))
> *category-list*))))
>
> (defmacro Item-classm (category &key opt-a opt-b)
> (let ((sym-category (intern category))
> (sym-class nil))
>
> (cond ((and category opt-a opt-b)
> (let ((sym-opt-a (intern opt-a))
> (sym-opt-b (INTERN opt-b)))
> (setf sym-class
> (INTERN
> (concatenate 'string category "-" opt-a "-" opt-b)))
> `(defclass ,sym-class (common ,sym-category ,sym-opt-a ,sym-opt-b )() )))
>
> ((and category opt-a )
> (let ((sym-opt-a (INTERN opt-a)))
> (setf sym-class (INTERN (concatenate 'string category "-" opt-a)))
> `(defclass ,sym-class (common ,sym-category ,sym-opt-a )() )))
>
> ((and category opt-b )
> (let ((sym-opt-b (INTERN opt-b)))
> (setf sym-class (INTERN (concatenate 'string category "-" opt-b)))
> `(defclass ,sym-class (common ,sym-category ,sym-opt-b )() )))
>
> (category
> (setf sym-class sym-category)
> `(defclass ,sym-class (common ,sym-category )() ))
>
> (T "Oopsies"))));learn about condition system

-- 
Thomas A. Russ,  USC/Information Sciences Institute