From: Bob Felts
Subject: Newbie Macro Question
Date: 
Message-ID: <1hc3e03.1ic3cms1m15rlsN%wrf3@stablecross.com>
Exercise 14.4 in Touretzky's book asks the student to write a macro
called SIMPLE-ROTATEF that switches the value of two variables.

The solution is easy:

(defmacro simple-rotatef (a b)
  `(let ((temp ,a))
     (setq ,a ,b)
     (setq ,b temp)))

However, when I run it with OpenMCL,  the macro works, but I get the
following:

;Compiler warnings :
;   Undeclared free variable A (2 references), in an anonymous lambda
form.
;   Undeclared free variable B (2 references), in an anonymous lambda
form.

How do I fix this?  The answer in the book has the same problem.

From: Kaz Kylheku
Subject: Re: Newbie Macro Question
Date: 
Message-ID: <1142203986.898212.223660@i39g2000cwa.googlegroups.com>
Bob Felts wrote:
> Exercise 14.4 in Touretzky's book asks the student to write a macro
> called SIMPLE-ROTATEF that switches the value of two variables.

(psetf a b b a)

> The solution is easy:
>
> (defmacro simple-rotatef (a b)
>   `(let ((temp ,a))
>      (setq ,a ,b)
>      (setq ,b temp)))

Bzzt! Parasitic capture!

 (macroexpand '(simple-rotatef temp tamp))

 --> (LET ((TEMP TEMP)) (SETQ TEMP TAMP) (SETQ TAMP TEMP))

> However, when I run it with OpenMCL,  the macro works, but I get the
> following:
>
> ;Compiler warnings :
> ;   Undeclared free variable A (2 references), in an anonymous lambda
> form.

This can't be referring to your macro's lambda parameter A.

Do you have a test case for your macro which uses A and B as parameters
for calling the macro, but does not define the variables A and B? I.e.
do you have the top level orm

  (simple-rotatef a b)

somewhere without a (defvar a ...) and (defvar b ...) above it?
From: Bob Felts
Subject: Re: Newbie Macro Question
Date: 
Message-ID: <1hc3v0r.1q23mph1gi5ueaN%wrf3@stablecross.com>
Kaz Kylheku <········@gmail.com> wrote:

> Bob Felts wrote:
> > Exercise 14.4 in Touretzky's book asks the student to write a macro
> > called SIMPLE-ROTATEF that switches the value of two variables.
> 
> (psetf a b b a)
> 
> > The solution is easy:
> >
> > (defmacro simple-rotatef (a b)
> >   `(let ((temp ,a))
> >      (setq ,a ,b)
> >      (setq ,b temp)))
> 
> Bzzt! Parasitic capture!
> 
>  (macroexpand '(simple-rotatef temp tamp))
> 
>  --> (LET ((TEMP TEMP)) (SETQ TEMP TAMP) (SETQ TAMP TEMP))
> 

Well, yes.  This is an introductory example of macros and things like
(gensym) haven't yet been covered.

> > However, when I run it with OpenMCL,  the macro works, but I get the
> > following:
> >
> > ;Compiler warnings :
> > ;   Undeclared free variable A (2 references), in an anonymous lambda
> > form.
> 
> This can't be referring to your macro's lambda parameter A.
> 
> Do you have a test case for your macro which uses A and B as parameters
> for calling the macro, but does not define the variables A and B? I.e.
> do you have the top level orm
> 
>   (simple-rotatef a b)
> 
> somewhere without a (defvar a ...) and (defvar b ...) above it?

Yes.  If I used defvar then everything worked.  I'm curious as to why it
"fails" when I define a symbol via (setf foo something) (setf bar
something-else) (simple-rotatef foo bar).  The interpreter knows foo and
bar exist without a defvar.
From: David Sletten
Subject: Re: Newbie Macro Question
Date: 
Message-ID: <7A5Rf.12625$pV5.3942@tornado.socal.rr.com>
Bob Felts wrote:

> 
> Yes.  If I used defvar then everything worked.  I'm curious as to why it
> "fails" when I define a symbol via (setf foo something) (setf bar
> something-else) (simple-rotatef foo bar).  The interpreter knows foo and
> bar exist without a defvar.
> 

Some of this stuff is confusing, so let's take it one step at a time. 
First, you understand now that there is nothing wrong with your macro. 
It's the "variables" you applied it to that are causing the problem. 
It's just a coincidence that the parameters of your macro are A and B 
and you used it with top-level "variables" A and B. Had you done:
(setq x 1 y 2)
(simple-rotatef x y)
You would have gotten similar warnings. By contrast, when you do:
(defvar a 8)
(defvar b 9)
(simple-rotatef a b)
You get no warnings.

Before we look at the real issue I wanted to touch on another problem. 
You mention above "defining a symbol" via SETF. This is a little sloppy. 
Symbols are typically created by the Lisp reader. Try this:
* (find-symbol "QWERTY")

NIL
NIL
* 'qwerty

QWERTY
* (find-symbol "QWERTY")

QWERTY
:INTERNAL
The very act of typing the symbol's name at the REPL causes the reader 
to create the symbol. A symbol is a data structure with several "cells". 
When you type 'x, the value is the symbol X, which appears to just be a 
character string, i.e., the name of the symbol. However, this is merely 
the printed representation:
* (type-of 'x)

SYMBOL

We typically use symbols to name variables (although variables need not 
have names--generalized variables). But in order for Lisp to understand 
what a name such as X "means" we have to associate the name with a 
variable. This is called a binding. A binding connects a name (symbol) 
with some storage in memory (a variable).

In Common Lisp we create bindings for lexical variables via LET or 
function calls (LAMBDA binding). We use DEFVAR and DEFPARAMETER to bind 
(global) special variables. There are no global lexical variables in 
Common Lisp, and you did not use DEFVAR in your first experiments, so 
technically you have not established a binding between your variable 
names A and B and any variables. By simply using SETQ you have jumped 
the gun. You can't assign a new value to a variable named A without 
letting Lisp know what variable A is supposed to refer to. You may have 
read in one of a number of well known Lisp books that you can "create" 
variables via SETQ but it's "bad style"--you should use DEFVAR 
(Touretzky, Graham, and Slade all make such claims). To make matters 
worse, many examples in the ANSI standard appear to do just this. For 
example:
http://www.lispworks.com/documentation/HyperSpec/Body/s_setq.htm
But as you can see this doesn't really make sense, and the behavior as 
Pascal pointed out is unspecified.

You may not be up to it just yet, but someday you'll just have to sit 
down and read section 3.1 of the HyperSpec:
http://www.lispworks.com/documentation/HyperSpec/Body/03_a.htm
This may be helpful too:
http://www.lispworks.com/documentation/HyperSpec/Body/t_symbol.htm

Aloha,
David Sletten
From: Pascal Bourguignon
Subject: Re: Newbie Macro Question
Date: 
Message-ID: <87r757o0og.fsf@thalassa.informatimago.com>
····@stablecross.com (Bob Felts) writes:

> Exercise 14.4 in Touretzky's book asks the student to write a macro
> called SIMPLE-ROTATEF that switches the value of two variables.
>
> The solution is easy:
>
> (defmacro simple-rotatef (a b)
>   `(let ((temp ,a))
>      (setq ,a ,b)
>      (setq ,b temp)))
>
> However, when I run it with OpenMCL,  the macro works, but I get the
> following:
>
> ;Compiler warnings :
> ;   Undeclared free variable A (2 references), in an anonymous lambda
> form.
> ;   Undeclared free variable B (2 references), in an anonymous lambda
> form.
>
> How do I fix this?  The answer in the book has the same problem.

The problem is not IN the macro!

[124]>  (defmacro simple-rotatef (a b)
   `(let ((temp ,a))
      (setq ,a ,b)
      (setq ,b temp)))
SIMPLE-ROTATEF
[125]> (defvar a 1)
A
[126]> (defvar b 2)
B
[127]> (simple-rotatef a b)
1
[128]> (list a b)
(2 1)
[129]> 



As for your macro, in Common Lisp, there's a PSETQ:

(defmacro simple-rotatef (a b) 
     `(psetq ,a ,b 
             ,b ,a))


-- 
__Pascal Bourguignon__                     http://www.informatimago.com/
-----BEGIN GEEK CODE BLOCK-----
Version: 3.12
GCS d? s++:++ a+ C+++ UL++++ P--- L+++ E+++ W++ N+++ o-- K- w--- 
O- M++ V PS PE++ Y++ PGP t+ 5+ X++ R !tv b+++ DI++++ D++ 
G e+++ h+ r-- z? 
------END GEEK CODE BLOCK------
From: Bob Felts
Subject: Re: Newbie Macro Question
Date: 
Message-ID: <1hc3g62.sanrqa1e6z0ioN%wrf3@stablecross.com>
Pascal Bourguignon <······@informatimago.com> wrote:

> ····@stablecross.com (Bob Felts) writes:
> 
> > Exercise 14.4 in Touretzky's book asks the student to write a macro
> > called SIMPLE-ROTATEF that switches the value of two variables.
> >
> > The solution is easy:
> >
> > (defmacro simple-rotatef (a b)
> >   `(let ((temp ,a))
> >      (setq ,a ,b)
> >      (setq ,b temp)))
> >
> > However, when I run it with OpenMCL,  the macro works, but I get the
> > following:
> >
> > ;Compiler warnings :
> > ;   Undeclared free variable A (2 references), in an anonymous lambda
> > form.
> > ;   Undeclared free variable B (2 references), in an anonymous lambda
> > form.
> >
> > How do I fix this?  The answer in the book has the same problem.
> 
> The problem is not IN the macro!
> 
> [124]>  (defmacro simple-rotatef (a b)
>    `(let ((temp ,a))
>       (setq ,a ,b)
>       (setq ,b temp)))
> SIMPLE-ROTATEF
> [125]> (defvar a 1)
> A
> [126]> (defvar b 2)
> B
> [127]> (simple-rotatef a b)
> 1
> [128]> (list a b)
> (2 1)
> [129]> 
> 
> 

I should have mentioned that I don't get the compiler warning if
variables are declared with defvar.  But if you do:

(setf c '(c c c))
(setf d '(d d d))
(simple-rotatef c d)

then you get the warnings and I'd like to understand why.
From: jayessay
Subject: Re: Newbie Macro Question
Date: 
Message-ID: <m3irqj2pzk.fsf@rigel.goldenthreadtech.com>
····@stablecross.com (Bob Felts) writes:

> I should have mentioned that I don't get the compiler warning if
> variables are declared with defvar.  But if you do:
> 
> (setf c '(c c c))
> (setf d '(d d d))
> (simple-rotatef c d)
> 
> then you get the warnings and I'd like to understand why.

Because using undeclared variables is specified as yielding undefined
(i.e., implementation defined) results.


/Jon

-- 
'j' - a n t h o n y at romeo/charley/november com
From: Pascal Bourguignon
Subject: Re: Newbie Macro Question
Date: 
Message-ID: <87irqjnv58.fsf@thalassa.informatimago.com>
····@stablecross.com (Bob Felts) writes:
> I should have mentioned that I don't get the compiler warning if
> variables are declared with defvar.  But if you do:
>
> (setf c '(c c c))
> (setf d '(d d d))
> (simple-rotatef c d)
>
> then you get the warnings and I'd like to understand why.

Because that's the way it is: dynamic variables must be defined with
DEFVAR or DEFPARAMETER.  If you use SETF or SETQ or similar to assign
to dynamic variables that haven't been declared, then the behavior is
unspecified, and anything can happen, like the end of the universe(*),
or, if you're lucky, and error message.


If you don't want to declare dynamic variables, you can use lexical variables:

(let  ((c '(c c c))
       (d '(d d d)))
  (simple-rotatef c d)
  (print c)
  (print d))


Note that there's this convention of naming dynamic variables with stars:

(defparameter *a* '(a a a)) ; I should have written
(defparameter *b* '(b b b))
(simple-rotatef *a* *b*)


(Note that defvar doesn't re-assign the variable if it already exist,
you must use defparameter for that.)



(*) Yes, Common Lisp is THAT powerfull! ;-)
-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

"A TRUE Klingon warrior does not comment his code!"
From: Benjamin Teuber
Subject: Re: Newbie Macro Question
Date: 
Message-ID: <dv266p$buv$1@kohl.informatik.uni-bremen.de>
Bob Felts wrote:
> Exercise 14.4 in Touretzky's book asks the student to write a macro
> called SIMPLE-ROTATEF that switches the value of two variables.
> 
> The solution is easy:
> 
> (defmacro simple-rotatef (a b)
>   `(let ((temp ,a))
>      (setq ,a ,b)
>      (setq ,b temp)))
> 

If you use setf instead of setq, you can also use your mac like this:

(setf list '(a b c d))
(simple-rotatef (first list) (third list))

--> '(c b a d)


Also, your macro does nonsense if one of your vars has the name temp.
So it would be cleaner to gensym on expansion time:

(defmacro less-simple-rotatef (a b)
   (let ((temp (gensym)))
      `(let ((,temp ,a))
          (setf ,a ,b)
          (setf ,b temp))))

But of course this can be avoided using the simple psetf.

At first, I thought a and b get evaluated twice here if they are 
non-trivial places (or symbol-macros) and that this might cause the 
warning. But that seems incorrect as one of these times, it will 
actually be some function named (setf ..) which is called, so I guess 
there can't be "less evaluation", right?
Or am I overlooking some crazy advanced-setf-stuff that could be used here?
From: Pascal Bourguignon
Subject: Re: Newbie Macro Question
Date: 
Message-ID: <87ek17nux3.fsf@thalassa.informatimago.com>
Benjamin Teuber <······@web.de> writes:
> At first, I thought a and b get evaluated twice here if they are
> non-trivial places (or symbol-macros) and that this might cause the
> warning. But that seems incorrect as one of these times, it will
> actually be some function named (setf ..) which is called, so I guess
> there can't be "less evaluation", right?
> Or am I overlooking some crazy advanced-setf-stuff that could be used here?

You're overlooking this:


(defmacro less-simple-rotatef (a b)
   (let ((temp (gensym)))
      `(let ((,temp ,a))
          (setf ,a ,b)
          (setf ,b ,temp))))

(defparameter *a* (make-array 10 :initial-contents '(1 2 3 4 5 6 7 8 9 0)))
(defparameter *b* -1)
(defparameter *i* 0)

[146]> (LESS-SIMPLE-ROTATEF (aref *a* (incf *i*)) *b*)
2
[148]> (values *a* *b* *i*)
#(1 2 -1 4 5 6 7 8 9 0) ;
2 ;
2

Instead of: #(1 -1 3 4 5 6 7 8 9 0) ;


As you can see, you're incrementing twice *i*:

[150]> (setf *print-circle* nil)
NIL
[151]> (macroexpand-1 '(LESS-SIMPLE-ROTATEF (aref *a* (incf *i*)) *b*))
(LET ((#:G6377 (AREF *A* (INCF *I*))))
    (SETF (AREF *A* (INCF *I*)) *B*)
    (SETF *B* #:G6377)) ;
T

The correct way is indeed to use setf-expanders.

-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

"A TRUE Klingon warrior does not comment his code!"
From: Benjamin Teuber
Subject: Re: Newbie Macro Question
Date: 
Message-ID: <dv2915$clh$1@kohl.informatik.uni-bremen.de>
Pascal Bourguignon wrote:
> You're overlooking this:
> 
> 
> (defmacro less-simple-rotatef (a b)
>    (let ((temp (gensym)))
>       `(let ((,temp ,a))
>           (setf ,a ,b)
>           (setf ,b ,temp))))
> 
> (defparameter *a* (make-array 10 :initial-contents '(1 2 3 4 5 6 7 8 9 0)))
> (defparameter *b* -1)
> (defparameter *i* 0)
> 
> [146]> (LESS-SIMPLE-ROTATEF (aref *a* (incf *i*)) *b*)
> 2
> [148]> (values *a* *b* *i*)
> #(1 2 -1 4 5 6 7 8 9 0) ;

Ah, thank you.

> The correct way is indeed to use setf-expanders.

Could you (or someone else) give some code example how to do it correct?
From: Pascal Bourguignon
Subject: Re: Newbie Macro Question
Date: 
Message-ID: <871wx7ntkl.fsf@thalassa.informatimago.com>
Benjamin Teuber <······@web.de> writes:

> Pascal Bourguignon wrote:
>> You're overlooking this:
>> (defmacro less-simple-rotatef (a b)
>>    (let ((temp (gensym)))
>>       `(let ((,temp ,a))
>>           (setf ,a ,b)
>>           (setf ,b ,temp))))
>> (defparameter *a* (make-array 10 :initial-contents '(1 2 3 4 5 6 7 8
>> 9 0)))
>> (defparameter *b* -1)
>> (defparameter *i* 0)
>> [146]> (LESS-SIMPLE-ROTATEF (aref *a* (incf *i*)) *b*)
>> 2
>> [148]> (values *a* *b* *i*)
>> #(1 2 -1 4 5 6 7 8 9 0) ;
>
> Ah, thank you.
>
>> The correct way is indeed to use setf-expanders.
>
> Could you (or someone else) give some code example how to do it correct?


I've got this, to be able to write: (setf (nthcdr index list) new)

(define-setf-expander nthcdr (n place &environment env)
  ;;  Kalle Olavi Niemitalo <···@iki.fi>
  (multiple-value-bind (vars vals store-vars writer-form reader-form)
      (get-setf-expansion place env)
    (let* ((gn (gensym))
           (gstore (if store-vars (first store-vars) (gensym "STORE"))))
      (values (list* gn vars)
              (list* n vals)
              (list gstore)
              ;; Intentionally leaves 0.0 unrecognized.
              `(if (eql ,gn 0)
                   ,(case (length store-vars)
                      (0 `(progn ,writer-form ,gstore))
                      (1 writer-form)
                      (t `(let ,(rest store-vars)
                            (values ,writer-form))))
                 (setf (cdr (nthcdr (1- ,gn) ,reader-form)) ,gstore))
              `(nthcdr ,gn ,reader-form)))))


;; To handle (incf (nthcdr 0 foo)), you need
;; DEFINE-SETF-EXPANDER rather than DEFSETF.  Implementations
;; have been posted at least twice in this newsgroup:
;; 
;; 1998-10-21 <····················@engc.bu.edu>
;; 2004-10-30 <···················@Astalo.kon.iki.fi> 
;; 
;; To satisfy point 13 of CLHS section 11.1.2.1.2 (assuming CLiki
;; issue DEFINE-SETF-METHOD:FIX), you'd also have to shadow NTHCDR.


-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

"Do not adjust your mind, there is a fault in reality"
 -- on a wall many years ago in Oxford.
From: Alan Crowe
Subject: Re: Newbie Macro Question
Date: 
Message-ID: <86d5gqowf4.fsf@cawtech.freeserve.co.uk>
Benjamin Teuber <······@web.de> writes:
> Pascal Bourguignon wrote:
> 
> > The correct way is indeed to use setf-expanders.
> 
> Could you (or someone else) give some code example how to do it correct?

CL-USER> (defmacro displace (place new-value)
           (multiple-value-bind
                 (once-only-vars
                  once-only-forms
                  list-of-store-var
                  writer-form
                  reader-form)
               (get-setf-expansion place)
               `(let* ,(mapcar #'list
                               once-only-vars
                               once-only-forms)
                 (let (,(car list-of-store-var))
                   (prog1
                       ,reader-form
                     (setf ,(car list-of-store-var) ,new-value)
                   ,writer-form)))))
DISPLACE

CL-USER> (defvar a (vector 1 2 3))
A
CL-USER> (defvar i 0)
I
CL-USER> (displace (aref a (incf i)) 4)
2
CL-USER> (displace (aref a (incf i)) 4)
3
CL-USER> a
#(1 4 4)
CL-USER> (defmacro swap(left right)
           (multiple-value-bind
                 (left-vars
                  left-vals
                  left-store-vars
                  left-writer-form
                  left-reader-form)
               (get-setf-expansion left)
             (multiple-value-bind
                   (right-vars
                    right-vals
                    right-store-vars
                    right-writer-form
                    right-reader-form)
                 (get-setf-expansion right)
               `(let* ,(append (mapcar #'list
                                       left-vars
                                       left-vals)
                               (mapcar #'list
                                       right-vars
                                       right-vals))
                 (let ((,(car right-store-vars) ,left-reader-form)
                       (,(car left-store-vars) ,right-reader-form))
                   ,left-writer-form
                   ,right-writer-form
                   'swapped)))))
SWAP
CL-USER> (macroexpand-1 '(swap a b))
(LET* ()
  (LET ((#:G1625 A) (#:G1624 B))
    (SETQ A #:G1624)
    (SETQ B #:G1625)
    'SWAPPED))
T
CL-USER> (setf a (vector 1 2 3))
#(1 2 3)
CL-USER> (swap (aref a (print 0))
               (aref a (print 2)))

0 
2 

SWAPPED
CL-USER> a
#(3 2 1)
CL-USER> (macroexpand-1 ++)
(LET* ((#:G1634 A) (#:G1633 (PRINT 0)) (#:G1637 A) (#:G1636 (PRINT 2)))
  (LET ((#:G1635 (AREF #:G1634 #:G1633)) (#:G1632 (AREF #:G1637 #:G1636)))
    (LISP::%ASET #:G1634 #:G1633 #:G1632)
    (LISP::%ASET #:G1637 #:G1636 #:G1635)
    'SWAPPED))
T

Hope this code is correct.

Alan Crowe
Edinburgh
Scotland