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

Re: Apply



>I too discovered that apply and funcall don't work for lambda lists any
>more and was rather disheartened since a great deal of our user interface
>code relies on constructing functions which are then attached to buttons.
>
>Much of this code is created with backquotes so that variables can be
>insterted, a simple example might be:
>
>(defun make-menu-items (items)
>  (let (menu-items)
>    (dolist (item items)
>      (push (make-instance ...
>                           :attached-function
>                           `(lambda (window)
>			      (declare (ignore window))
>                              (print ',item)))))))
>
>I know the code isn't quite right but you get the idea, each lambda
>expression is created at run time.
>
>The solution I have adopted (but don't like) is to make the function
>dispatcher more intelligent and do the following:
>
>;; attached-function & window are bound in preceding lines
>  (funcall (if (listp attached-function)
>              (eval `(function ,attached-function))
>              attached-function)
>           window)
>
>This allows my dispatcher to deal with all the things that funcall (and
>apply used to deal with), but I always thought it was bad form to use eval
>in the middle of code? ...

Your particular example doesn't need to use EVAL.  Just use closures.

(defun make-menu-items (items)
  (let (menu-items)
    (dolist (item items)
      (push (make-instance ...
                           :attached-function
                           (let ((item item))
                             #'(lambda (window)
			                              (declare (ignore window))
                                 (print item))))))))

The reason for the seemingly spurious binding of ITEM has to do with the
fact that DOLIST does it's iteration by SETQ'ing the iteration variable. 
An alternative, cleaner way to do this is:

(defun make-menu-items (items)
  (let (menu-items)
    (mapc #'(lambda (item)
              (push (make-instance ...
                       :attached-function
                       #'(lambda (window)
			                        (declare (ignore window))
                           (print item)))))
           items)))

See the discussion on pp. 115-118 of CLtL II for more information about
closures.

You are right that you need EVAL or COMPILE to turn arbitrary list
structure into an object which is FUNCTIONP (as required by APPLY). 
Fortunately, you don't need that most of the time.  You'll find that
closures cover most of what you need.  Thay are also much more time- and
space-efficient than using list expressions.