From: ·······@abmx.rz.rwth-aachen.de
Subject: another macro question
Date: 
Message-ID: <2ntnni$pti@urmel.informatik.rwth-aachen.de>
Some days ago Alexander Scotts asks for the right way to write
a macro which takes a list of symbols and expands to something
that binds each symbol to a function definition using defun.

The posted solution was

(DEFMACRO defsums (symbol-list)

 `(PROGN ,@(MAPCAR #'(LAMBDA (symbol)
                        `(DEFUN ,symbol (x y) (+ x y)) )
                   symbol-list )) )

and works fine. I thought the explanations were very helpful to me. But 
nevertheless at the point i changed the code ...
(Macros are driving me crazy).

I tried to use it in another way to bind symbols to instances of a
class because the number of needed instances is available only
at run time, so:

(DEFMACRO make-instances (symbol-list)

 `(PROGN ,@(MAPCAR #'(LAMBDA (symbol)
                        `(SETQ ,symbol (make-instance 'bgl))
                   symbol-list )) )


So far so good. To create the symbol-list inside the macro i wrote


(DEFVAR *bgl-instances* NIL)

(DEFMACRO make-instances (n)

 `(PROGN (DOTIMES (i ,n) (PUSH (GENSYM "b") *bgl-instances*))

         ,@(MAPCAR #'(LAMBDA (symbol)
                        `(SETQ ,symbol (make-instance 'bgl))
                   *bgl-instances* )) )


I wonder why the expansion for (make-instances 2) gives

(PROGN (DOTIMES (i 2) (PUSH (GENSYM "b") *bgl-instances*)))


where's the rest an what about (SETQ a 2) (make-instances a) ?


The function to send messages to the instances is

(send-message <instance-name> <message-name> {args}*)


So i defined a new function, but i don't know how to avoid the eval
(even with a macro)

(DEFUN $send-bgl (i &REST args)

 (LET ( (inst (EVAL (NTH (1- i) *bgl-instances*))))

      (PUSH inst args)
      (APPLY #'send-message args) ) )

so ($send-bgl 1 :describe) gives a description of the instance bound to the
first symbol in *bgl-instances* for example.


My current solution is the above function send-bgl and the following


(DEFUN make-bgl-instances (n)

 (SETQ *bgl-instances* NIL)

 (DOTIMES (i n)
    (PUSH (GENSYM "b") *bgl-instances*) )

 (make-bgl-instances-2) )
 

(DEFMACRO make-bgl-instances-2 ()

 `(PROGN ,@(MAPCAR #'(LAMBDA (symbol)
                             `(SETQ ,symbol (MAKE-$INSTANCE 'bgl)) )
                   *bgl-instances* ) ) )


Calling only (make-bgl-instances-2) with *symbol-list* bound to a list of
symbols each returned by GENSYM gives what i want.

For (make-bgl-instances-2 2) ($send-bgl 1 :describe) signals the error that
the symbols in *bgl-instances* are bound to nothing. Though the list
*bgl-instances* is created with the same function make-bgl-instances only 
without calling make-bgl-instances-2.

What's going on ?
Are there any ideas out there ? It's really urgent !

Many, many thanks, Nummi ...
·······@rwth-aachen.de

From: Thomas A. Russ
Subject: Re: another macro question
Date: 
Message-ID: <TAR.94Apr6092031@grover.ISI.EDU>
OK, the fundamental issue you need to understand is WHEN forms are
evaluated in a macro.  The code in a macro body is executed at
macroexpansion time and returns a form which is then compiled (or
interpreted).  In your example:

In article <...> ·······@abmx.rz.rwth-aachen.de writes:
 > 
 > (DEFVAR *bgl-instances* NIL)
 > 
 > (DEFMACRO make-instances (n)
 > 
 >  `(PROGN (DOTIMES (i ,n) (PUSH (GENSYM "b") *bgl-instances*))
 > 
 >          ,@(MAPCAR #'(LAMBDA (symbol)
 >                         `(SETQ ,symbol (make-instance 'bgl))
 >                    *bgl-instances* )) )
 > 
          ^^^^ The ",@" evaluates the mapcar form at macroexpansion
time, which is BEFORE the dotimes form is run.  At macroexpansion time
the variable *bgl-instances* is NIL from the defvar.  It has not yet had
any symbols pushed onto it.

 > I wonder why the expansion for (make-instances 2) gives
 > 
 > (PROGN (DOTIMES (i 2) (PUSH (GENSYM "b") *bgl-instances*)))

Because that is the correct expansion.  The mapcar maps over NIL and
thus produces nothing.

[Aside:  I don't think you want to use GENSYM here.  I think you would 
 want GENTEMP instead.  The difference is that gentemp interns its
 symbols, so that you can access them by typing the names.  Gensym
 doesn't intern, so the names it generates can't be used to access
 the symbol.]

[Second aside:  You don't need a macro for what you are doing, the
  following function will also work:

(defun make-instances (n)
  (dotimes (i n)
    (let ((new-symbol (gentemp "B")))
	(set new-symbol (make-instance 'bgl))   ; Not setQ  !!!
	(push new-symbol *bgl-instances*))))

 > The function to send messages to the instances is
 > 
 > (send-message <instance-name> <message-name> {args}*)
 > 
 > 
 > So i defined a new function, but i don't know how to avoid the eval
 > (even with a macro)
 > 
 > (DEFUN $send-bgl (i &REST args)
 > 
 >  (LET ( (inst (EVAL (NTH (1- i) *bgl-instances*))))
 > 
 >       (PUSH inst args)
 >       (APPLY #'send-message args) ) )
 > 
 > so ($send-bgl 1 :describe) gives a description of the instance bound to the
 > first symbol in *bgl-instances* for example.
 > 

Consider a redesign.  There isn't any reason to store symbols bound to
the instances in your list.  You can just store the instances
themselves.

(defun make-instances (n)
  (dotimes (i n)
     (push (make-instance 'bgl) *bgl-instances*)))

(defun $send-bgl$ (i message &rest args)
  (apply #'send-message (nth (1- i)) message args))

Lisp deals well with objects and does not require variables in the way
other programming languages require them.


--
________________________________________________________________________
Thomas A. Russ,  Senior Research Scientist                   ···@isi.edu    
USC/Information Sciences Institute                  WWW:  http://isi.edu
4676 Admiralty Way, Marina del Rey, CA 90292          (310) 822-1511x775
From: Barry Margolin
Subject: Re: another macro question
Date: 
Message-ID: <2o5nvsINN809@early-bird.think.com>
In article <··········@urmel.informatik.rwth-aachen.de> ·······@abmx.rz.rwth-aachen.de writes:
>I tried to use it in another way to bind symbols to instances of a
>class because the number of needed instances is available only
>at run time, so:

The macro expansion can't depend on something at runtime, since it is
expanded at compile time.

>(DEFVAR *bgl-instances* NIL)
>
>(DEFMACRO make-instances (n)
> `(PROGN (DOTIMES (i ,n) (PUSH (GENSYM "b") *bgl-instances*))
>         ,@(MAPCAR #'(LAMBDA (symbol)
>                        `(SETQ ,symbol (make-instance 'bgl))
>                   *bgl-instances* )) )
>
>
>I wonder why the expansion for (make-instances 2) gives
>
>(PROGN (DOTIMES (i 2) (PUSH (GENSYM "b") *bgl-instances*)))
>
>where's the rest an what about (SETQ a 2) (make-instances a) ?

Since *BGL-INSTANCES* is empty when the macro expands, the MAPCAR maps over
an empty list and returns an empty list as the result.  This empty result
gets spliced into the PROGN and disappears.  The gensyms won't get added to
*BGL-INSTANCES* until the expansion is executed at runtime.

If you want the gensyms to be added to the list at expansion time then the
DOTIMES should be outside the backquoted list, e.g.

(defmacro make-instances (n)
  (dotimes (i n) (push (gensym "b") *bgl-instances*))
  `(progn ,@(mapcar #'(lambda (symbol) `(setq ,symbol (make-instance 'bgl)))
                    *bgl-instances*)))

But in this case (setq a 2) (make-instances a) won't work, because the
value of N will be A, so it will try to do (dotimes (i 'a) ...), which
obviously won't work.

Why do you need this to be a macro?  If it's dependent on something at run
time, then it should be a function:

(defun make-instances (n)
  (dotimes (i n)
    (let ((symbol (gensym "b")))
      (push symbol *bgl-instances*)
      (setf (symbol-value symbol) (make-instance 'bgl)))))

>The function to send messages to the instances is
>
>(send-message <instance-name> <message-name> {args}*)
>
>
>So i defined a new function, but i don't know how to avoid the eval
>(even with a macro)
>
>(DEFUN $send-bgl (i &REST args)
> (LET ( (inst (EVAL (NTH (1- i) *bgl-instances*))))
>      (PUSH inst args)
>      (APPLY #'send-message args) ) )
>
>so ($send-bgl 1 :describe) gives a description of the instance bound to the
>first symbol in *bgl-instances* for example.

To avoid the EVAL, use SYMBOL-VALUE.

However, a better solution would be to put the instances themselves in
*BGL-INSTANCES*.  For some reason, this unnecessary use of symbols seems to
be a common problem among beginning Lisp programmers -- they don't grasp
that you can simply put objects in data structures and use them directly.

(defun make-instances (n)
  (dotimes (i n)
    (push (make-instance 'bgl) *bgl-instances*)))

(defun $send-bgl (i &rest args)
  (apply #'send-message
         (nth (1- i) *bgl-instances*)
         args))

You don't need to (push inst args) because APPLY allows you to include
regular arguments ahead of the list-of-arguments argument.

As I've said before, if a beginner thinks he need to use EVAL, he's
probably doing something wrong.  If you're using the value cell of a
gensym, you're really doing something wrong.  If you don't care what the
variable's name is, then you could just as easily be using some other data
structure to hold the object.  Gensyms are mostly used to hold temporary
names used in the expansion of a macro -- if they're being used outside the
macro expansion (like being put on a permanemt list like *BGL-INSTANCES*)
then the design is wrong.

-- 
Barry Margolin
System Manager, Thinking Machines Corp.

······@think.com          {uunet,harvard}!think!barmar