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

Re: Apply



> 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? ...

Much better would be to flush that hacked up function dispatcher and instead 
either use closures, i.e.

  (defun make-menu-items (items)
    (let (menu-items)
      (dolist (item items)
        (let ((closed-item item))      ; ensure new binding for each item
          (push (make-instance ...
                  :attached-function #'(lambda (window)
                                         (declare (ignore window))
                                         (print closed-item)) ; close over item
                  ...)
                ...

or if you *really* need to be computing a lambda-expression, do the coercion 
once there rather than redoing it each time you try to invoke it.  Also, the 
"approved" way to do the coercion is

  (COERCE lambda-expression 'FUNCTION)

(see CLtL2, p.65).  One warning about this however; MCL2.0 final contains a 
buggy compiler-macro for this particular use of coerce.  I expect this will be 
fixed in some future patch, and in the meantime you can do what I did to work 
around the problem, i.e. use something like the following function

  ;; MCL2.0 compiler-macro for coerce to function is buggy, so declare
  ;; coerce notinline to inhibit the transformation.
  (defun coerce-lambda-expression-to-function (lambda-expression)
    (declare (notinline coerce))
    (coerce lambda-expression 'function))

ps. The line "(let ((closed-item item)) ..." is needed because you don't (in 
general) know whether dolist establishes a fresh binding for each iteration or 
uses the same binding repeatedly (by setq'ing it for each iteration).