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