From: Jacek Generowicz
Subject: CMUCL macro problem
Date: 
Message-ID: <tyfit02r3zg.fsf@pcitapi22.cern.ch>
I have a macro which, in CMUCL, appears to result in
the evaluation of something different from its macro expansion.

[The macro takes function definitions and messes with them; I've
removed most of its meat, leaving just what is necessary to still
exhibit the puzzling behaviour, and called the reduced macro "wtf".]

For example, let's look at a macroexpansion

     * (macroexpand 
      '(wtf (defun y (a b c) (+ a b c))))
     (LET ((Y 0.0d0))
       (DEFUN Y-OPT (A B C) (+ A B C)))
     T

Copying, pasting and evaluating the expansion given above, I get the
result I expect:

     * (LET ((Y 0.0d0))
       (DEFUN Y-OPT (A B C) (+ A B C)))
     Converted Y-OPT.
     
     In: LET ((Y 0.0d0))
       (LET ((Y 0.0d0))
         (DEFUN Y-OPT (A B C) (+ A B C)))
     Note: Variable Y defined but never used.
     
     Y-OPT

But, if I simply run the macro with the same argument, the modified
function name has an extra "-opt", as does the variable "y":

     * (wtf (defun y (a b c) (+ a b c)))
     Converted Y-OPT-OPT.
     
     In: WTF (DEFUN Y-OPT-OPT (A B C) (+ A B C))
       (WTF (DEFUN Y-OPT-OPT (A B C) (+ A B C)))
     ==>
       (LET ((Y-OPT 0.0d0))
         (DEFUN Y-OPT-OPT (A B C) (+ A B C)))
     Note: Variable Y-OPT defined but never used.
     
     Y-OPT-OPT

In Clisp, running the macro gives the result I would normally expect:

     [4]> (wtf (defun y (a b c) (+ a b c)))
     Y-OPT


For completeness, I include the source code of wtf:

(defun change-function-name (function-definition)
  "(DEFUN NAME ...) --> (DEFUN NAME-OPT ...)"
  (let ((new-name (intern
		   (concatenate
		    'string
		    (symbol-name (second function-definition))
		    "-OPT"))))
    (setf (second function-definition) new-name))
  function-definition)

(defun change-function-names (function-definitions)
  (mapcar #'change-function-name function-definitions))

(defmacro wtf (&body function-definitions)
  (let* ((function-names (mapcar #'second function-definitions))
	 (renamed-definitions
	  (change-function-names function-definitions)))
    `(let ,(mapcar (lambda (x) (list x 0.d0)) function-names)
       ,@renamed-definitions)))

From: Fred Gilham
Subject: Re: CMUCL macro problem
Date: 
Message-ID: <u7d6qa4cc2.fsf@snapdragon.csl.sri.com>
> But, if I simply run the macro with the same argument, the modified
> function name has an extra "-opt", as does the variable "y":


Sure looks like a multiple-evaluation problem to me.  Every time wtf
is expanded, it will call change-function-names again.  There's no
guarantee how many times a macro will be expanded.  This would explain
why you get the -opt-opt effect --- wtf is getting expanded multiple
times.

Macros are supposed to be idempotent (Paul Graham calls this
'functional').  That is, they should give the same result every time
they're called with the same arguments.

You might be able to see what's going on by tracing
change-function-name. Hmmn, let's see....




CMU Common Lisp 18d+ 13-Sep-2002, running on snapdragon.csl.sri.com
Send questions and bug reports to gilham, 
or see <URL:http://www.cons.org/cmucl/support.html>.
Loaded subsystems:
    Python 1.0, target Intel x86
    CLOS based on PCL version:  September 16 92 PCL (f)
    Gray Streams Protocol Support
    CLX X Library MIT R5.02
* 
(defun change-function-name (function-definition)
  "(DEFUN NAME ...) --> (DEFUN NAME-OPT ...)"
  (let ((new-name (intern
                   (concatenate
                    'string
                    (symbol-name (second function-definition))
                    "-OPT"))))
    (setf (second function-definition) new-name))
  function-definition)

CHANGE-FUNCTION-NAME
* 
(defun change-function-names (function-definitions)
  (mapcar #'change-function-name function-definitions))

CHANGE-FUNCTION-NAMES
* 
(defmacro wtf (&body function-definitions)
  (let* ((function-names (mapcar #'second function-definitions))
         (renamed-definitions
          (change-function-names function-definitions)))
    `(let ,(mapcar (lambda (x) (list x 0.d0)) function-names)
       ,@renamed-definitions)))


WTF
* (trace change-function-names)

(CHANGE-FUNCTION-NAMES)
*  (wtf (defun y (a b c) (+ a b c)))

  0: (CHANGE-FUNCTION-NAMES ((DEFUN Y # #)))
  0: CHANGE-FUNCTION-NAMES returned ((DEFUN Y-OPT # #))
  0: (CHANGE-FUNCTION-NAMES ((DEFUN Y-OPT # #)))
  0: CHANGE-FUNCTION-NAMES returned ((DEFUN Y-OPT-OPT # #))
Converted Y-OPT-OPT.

In: WTF (DEFUN Y-OPT-OPT (A B C) (+ A B C))
  (WTF (DEFUN Y-OPT-OPT (A B C) (+ A B C)))
==>
  (LET ((Y-OPT 0.0d0))
    (DEFUN Y-OPT-OPT (A B C) (+ A B C)))
Note: Variable Y-OPT defined but never used.
Y-OPT-OPT
* 

Yes, it's a multiple-evaluation problem.

-- 
Fred Gilham                                      ······@csl.sri.com
Ah, the 20th century, when the flight from reason crash-landed into
the slaughterhouse.  --- James Ostrowski
From: Jacek Generowicz
Subject: Re: CMUCL macro problem
Date: 
Message-ID: <tyfadleqx1v.fsf@pcitapi22.cern.ch>
Jacek Generowicz <················@cern.ch> writes:

> I have a macro which, in CMUCL, appears to result in the evaluation
> of something different from its macro expansion.

Thanks to Paul Foley for pointing out by email that I was modyfying my
source function definitions when doing 

   (setf (second function-definition) new-name))

in the function

   (defun change-function-name (function-definition)
     "(DEFUN NAME ...) --> (DEFUN NAME-OPT ...)"
     (let ((new-name (intern
                     (concatenate
                      'string
                      (symbol-name (second function-definition))
                      "-OPT"))))
       (setf (second function-definition) new-name))
     function-definition)