[comp.lang.lisp] macro misbehaviour

Jenny.Rowland@levels.sait.edu.au (06/09/91)

Please help..... I use VAX Lisp, and have come across some annoying behaviour.
I would like to know:
1. why it occurs;        (I suspect that it is some kind of optimization)
2. how to get around it.

The Problem:
Given the following macros and function:

    (DEFMACRO TRY (X) (LET ((A (EVAL X))) `(TT ,(MAPCAR #'ADD-1 A))))

    (DEFUN ADD-1 (X) (1+ X))

    (DEFMACRO TT (X) `(FORMAT T "~a ~%" ',X))

and the following code:

   (dotimes (n 3)
     (let ((a (list n n n)))
       (try a)))

I get the following results:

   (1 1 1)
   (1 1 1)
   (1 1 1)

when I would expect:

   (1 1 1)
   (2 2 2)
   (3 3 3)

It appears that the first call is evaluated, but further calls are 'recognised'
as being the same as previous calls (despite the fact that the value of
n and therefore a has changed), bypasses evaluation of the mapcar form, and
sends the previous result to macro tt.

Has anyone and explanation? and HOW CAN I GET AROUND IT???


Regards, and thanks in advance,
Jenny Rowland

--------

Jenny.rowland@sait.edu.au
University of South Australia

.....You play, you win, you play, you lose, you play...
                                                      J Winterson

barmar@think.com (Barry Margolin) (06/09/91)

In article <16456.285261a2@levels.sait.edu.au> Jenny.Rowland@levels.sait.edu.au writes:
>    (DEFMACRO TRY (X) (LET ((A (EVAL X))) `(TT ,(MAPCAR #'ADD-1 A))))
>    (DEFUN ADD-1 (X) (1+ X))
>    (DEFMACRO TT (X) `(FORMAT T "~a ~%" ',X))
>   (dotimes (n 3)
>     (let ((a (list n n n)))
>       (try a)))
>
>I get the following results:
>
>   (1 1 1)
>   (1 1 1)
>   (1 1 1)
>
>when I would expect:
>
>   (1 1 1)
>   (2 2 2)
>   (3 3 3)

The problem is your use of EVAL.  I'm surprised you didn't get an unbound
variable error regarding A; I suspect you had previously done

	(setq a '(0 0 0))

Unless you proclaim A special, LET binds it as a lexical variable.  EVAL
operates in the null lexical environment, so it isn't affected by this
binding.

However, even if you were to proclaim it special you might see this kind of
behavior.  Compilers must, and some interpreters do, expand macros before
executing any of the code.  Thus, the macro would be evaluated prior to
binding the variable in the LET.

This is why it is almost always wrong to use EVAL in macros.
-- 
Barry Margolin, Thinking Machines Corp.

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

gt4084c@prism.gatech.EDU (SRINIVASAN,K) (06/10/91)

I have a similar problem too.  

I wrote the follwing macro for reading a file and defining classes.
During any single iteration, the three calls to "read" read three
succesive symbols from the file as you would expect.  However, during
successive iterations, the same set of three symbols are read repeatedly.
So, I get the same class defined 4 times instead of defining 4 different
classes.  If I put in 4 explicit calls contained in a progn form instead
of the do-loop, it works fine.  Can somebody please help me fix this 
problem.  Thanks in advance.

Define-Class is a macro calling defclass (written by Marty Hall of AAI Corp.,
and very kindly shared with me). 


(defmacro class-from-file (file-name)
  (with-open-file (input-stream file-name
				:direction :input)
    `(do ((q 4 (- q 1))) ((= q 0) nil)
      (Define-Class ,(read input-stream) ,(read input-stream) .,(read input-stream)))))


-- 
SRINIVASAN,K
School of Textile Engineering   Georgia Tech.
uucp: ...!{allegra,amd,hplabs,seismo,ut-ngp}!gatech!prism!gt4084c
ARPA: gt4084c@prism.gatech.edu

meehan@src.dec.com (Jim Meehan) (06/10/91)

    I wrote the following macro for reading a file and defining classes.
    During any single iteration, the three calls to "read" read three
    succesive symbols from the file as you would expect.  However,
    during successive iterations, the same set of three symbols are
    read repeatedly.  So, I get the same class defined 4 times instead
    of defining 4 different classes.  If I put in 4 explicit calls
    contained in a progn form instead of the do-loop, it works fine.
    Can somebody please help me fix this

    (defmacro class-from-file (file-name)
      (with-open-file (input-stream file-name
    				:direction :input)
        `(do ((q 4 (- q 1))) ((= q 0) nil)
          (Define-Class ,(read input-stream) ,(read input-stream)
                        .,(read input-stream)))))

The immediate problem is that there is only one set of 3 calls to READ
at macroexpansion time.  The loop iterates at runtime, not at
macroexpansion time.  To produce the effect you want, you'd have to
say something like this:

(defmacro class-from-file (file-name)
  (with-open-file (input-stream file-name)
    (do ((q 4 (- q 1))
         (code '()
               (cons `(Define-Class ,(read input-stream)
                                    ,(read input-stream)
                                    .,(read input-stream))
                     code)))
        ((= q 0) `(progn ,@(nreverse code))))))

[Are you sure about that ".," ?]

More generally, it is a BAD idea to write macros that have side-effects
like this. (An earlier example with EVAL was similarly afflicted.)

1. Macros are best thought of as shorthand, where the expansion is
entirely equivalent to the unexpanded form, in the sense in which
(cadr (baz)) is equivalent to (car (cdr (baz))).  Lots of software
tools ("code-walkers") may be interested in expanding macros.  The
compiler is one.  If you wrote (class-from-file "FOO") in a program
that you compiled, it would have opened the file and read the symbols
in the process of compiling.  A prettyprinter or a debugger might also
expand a macro; you probably don't want them opening the file.  If you
had looked at the expansion of the macro, e.g., via
    (macroexpand '(class-from file "FOO"))
you would have noticed that (A) the expander opened the file, and (B)
the expansion was incorrect.

2. You're using a macro only because you're trying to "call" another macro;
if Define-Class were a function, then class-from-file would be a
function, and you wouldn't have had a problem.

It may be that the macro (Define-Class x y z) expands into something
simple like (*Define-Class 'x 'y 'z), where *Define-Class is a function
that actually does all the work.  If that's the case, then you should
rewrite class-from-file as a function that calls *Define-Class 4 times:
no muss, no fuss, no backquotes.

If that's not an option, then you may want to define a separate "pass"
in which you make all your calls to class-from-file and similar
macro-calling macros, because that pass is *generating* Lisp code.
If you've ever used the C tools yacc and lex, you'll understand what
I mean.

krulwich@ils.nwu.edu (Bruce Krulwich) (06/10/91)

Two recently posted macro questions suffer from the same problem:

In article <16456.285261a2@levels.sait.edu.au>, Jenny.Rowland@levels writes:
>    (DEFMACRO TRY (X) (LET ((A (EVAL X))) `(TT ,(MAPCAR #'ADD-1 A))))
>    (DEFUN ADD-1 (X) (1+ X))
>    (DEFMACRO TT (X) `(FORMAT T "~a ~%" ',X))
>and the following code:
>   (dotimes (n 3)
>     (let ((a (list n n n)))
>       (try a)))
>I get the following results:
>   (1 1 1)
>   (1 1 1)
>   (1 1 1)
>It appears that the first call is evaluated, but further calls are
>'recognised' as being the same as previous calls (despite the fact that the
>value of n and therefore a has changed), bypasses evaluation of the mapcar
>form, and sends the previous result to macro tt.


In article <30996@hydra.gatech.EDU>, gt4084c@prism (SRINIVASAN,K) writes:
>During any single iteration, the three calls to "read" read three
>succesive symbols from the file as you would expect.  However, during
>successive iterations, the same set of three symbols are read repeatedly.
>So, I get the same class defined 4 times instead of defining 4 different
>classes.
>
>(defmacro class-from-file (file-name)
> (with-open-file (input-stream file-name
>                       :direction :input)
>   `(do ((q 4 (- q 1))) ((= q 0) nil)
>     (Define-Class ,(read input-stream) ,(read input-stream) 
>                   .,(read input-stream)))))

In each case there is some code that is only run once that "should" be run
several times.

The problem in each case is that computation is done at the time the macro is
expanded that should be done at the time the macro result is executed.  In
other words, the code for a DEFMACRO (i.e., the code which takes arguments and
returns a list which is later executed) can and should only be executed once,
and the list which it returns is then plugged into the program and executed as
often as desired.

In the above macros, the code which is only executed once (the MAPCAR in the
first example, and the WITH-OPEN-FILE / READ in the second) is executed at the
time the code is compiled (or parsed by the interpreter), and the result (the
result of MAPCARing at compile-time, or the thing read from the file at
compile-time) is plugged into the loops.

Hope this helps.

Bruce Krulwich
krulwich@ils.nwu.edu

 

barmar@think.com (Barry Margolin) (06/11/91)

In article <1991Jun9.215315.25797@src.dec.com> meehan@src.dec.com (Jim Meehan) writes:
>2. You're using a macro only because you're trying to "call" another macro;
...
>It may be that the macro (Define-Class x y z) expands into something
>simple like (*Define-Class 'x 'y 'z), where *Define-Class is a function
...
>If that's not an option, then you may want to define a separate "pass"
>in which you make all your calls to class-from-file

Actually, solving the problem of trying to "call" a macro is one of the few
excuses for using EVAL.

CLASS-FROM-FILE could be a function (*not* a macro) that looks something
like this:

(defun class-from-file (file-name)
  (with-open-file (input-stream file-name :direction :input)
    (dotimes (q 4)
      (eval `(define-class ,(read input-stream) ,(read input-stream)
		.,(read input-stream))))))
-- 
Barry Margolin, Thinking Machines Corp.

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

john@thelonius.mitre.org (John D. Burger) (06/14/91)

Bruce Krulwich (krulwich@ils.nwu.edu) writes:

  Two recently posted macro questions suffer from the same problem:
  In each case there is some code that is only run once that "should"
  be run several times.

  The problem in each case is that computation is done at the time the
  macro is expanded that should be done at the time the macro result
  is executed.  In other words, the code for a DEFMACRO (i.e., the
  code which takes arguments and returns a list which is later
  executed) can and should only be executed once, and the list which
  it returns is then plugged into the program and executed as often as
  desired.

In fact, an implementation may cache a macro expansion for a given
environment and set of arguments.  So, if twenty-seven functions
contain the code (BLAH X (+ Y Z)), the BLAH expander might only be
called ONCE.

I think it's important to think of a macro as a function that happens
to compute some Lisp code, NOT as a function to do whatever that Lisp
code will do.

--
John Burger                                               john@mitre.org

"You ever think about .signature files? I mean, do we really need them?"
  - alt.andy.rooney