From: cmo
Subject: which one of these with-gensym implementations is better?
Date: 
Message-ID: <1167154323.037577.204660@73g2000cwn.googlegroups.com>
Hi,

I've went through most of PCL (which is one of the engaging books,
thanks Peter), and I have read a couple of articles here and there on
the net about lisp.

one thing that my grabbed attention was the implementation of
with-gensym in PCL, and the one mentioned in 'Good lisp programming
style' document on the net.

PCL:

   (defmacro with-gensyms ((&rest names) &body body)
     `(let ,(loop for n in names collect `(,n (gensym)))
        ,@body))

GLPS:

   (defmacro with-gensyms (symbols body)
     (sublis (mapcar #'(lambda (sym)
   		      (cons sym (gensym (string sym)))) symbols)
   	  body))


the only difference that I noticed was the fact that you need to use
comma before variables in PCL with-gens while you do not need to do
that in the other implementation.

any other differences, insights.


Thanks in advance.

Regards

cmo-0

From: Rob Warnock
Subject: Re: which one of these with-gensym implementations is better?
Date: 
Message-ID: <XOadncjHBtXnQgzYnZ2dnUVZ_ue3nZ2d@speakeasy.net>
cmo <·······@gmail.com> wrote:
+---------------
| PCL: (defmacro with-gensyms ((&rest names) &body body)
|        `(let ,(loop for n in names collect `(,n (gensym)))
|           ,@body))
| 
| GLPS: (defmacro with-gensyms (symbols body)
|         (sublis (mapcar #'(lambda (sym)
|                             (cons sym (gensym (string sym))))
|                         symbols)
|    	          body))
| 
| the only difference that I noticed was the fact that you need to use
| comma before variables in PCL with-gens while you do not need to do
| that in the other implementation.
| any other differences, insights.
+---------------

Well, the second one has a bug, at least when being used in a Lisp2
[or LispN] such as Common Lisp -- the SUBLIS is not sensitive to
whether a symbol to be substituted in the body is in the function
or a value position, so the following happens:

    > (macroexpand-1
       '(with-gensyms (list)
	 (let ((list (list 1 2 :three)))
	   (list (length list) list))))

    (LET ((#:LIST1591 (#:LIST1591 1 2 :three)))
      (#:LIST1591 (LENGTH #:LIST1591) #:LIST1591))
    T
    > 

Oops! That's certainly not going to fly!  ;-}

Whereas the PCL one not only requires you to be explicit about
*which* instances of the symbol(s) should be substituted, it also
assumes you will put the BODY of the WITH-GENSYMS inside its own
backquote, e.g.:

    > (macroexpand-1
       '(with-gensyms (list)
	 `(let ((,list (list 1 2 :three)))  ; Note the initial backquote!
	    (list (length ,list) ,list))))

    (LET ((LIST (GENSYM)))
      `(LET ((,LIST (LIST 1 2 :three)))
	 (LIST (LENGTH ,LIST) ,LIST)))
    T
    > (eval *)

    (LET ((#:G1610 (LIST 1 2 :three)))
      (LIST (LENGTH #:G1610) #:G1610))
    > (eval *)

    (3 (1 2 :THREE))
    > 

Capische?


-Rob

-----
Rob Warnock			<····@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607
From: Ravi
Subject: Re: which one of these with-gensym implementations is better?
Date: 
Message-ID: <1167226663.295992.50120@i12g2000cwa.googlegroups.com>
Great answer Rob! Very educational!
ravi

Rob Warnock wrote:

>
From: Alan Crowe
Subject: Re: which one of these with-gensym implementations is better?
Date: 
Message-ID: <86fyb12d6h.fsf@cawtech.freeserve.co.uk>
"cmo" <·······@gmail.com> writes:
> PCL:
> 
>    (defmacro with-gensyms ((&rest names) &body body)
>      `(let ,(loop for n in names collect `(,n (gensym)))
>         ,@body))
> 
> GLPS:
> 
>    (defmacro with-gensyms (symbols body)
>      (sublis (mapcar #'(lambda (sym)
>    		      (cons sym (gensym (string sym)))) symbols)
>    	  body))
> 
> 
> the only difference that I noticed was the fact that you need to use
> comma before variables in PCL with-gens while you do not need to do
> that in the other implementation.
> 
> any other differences, insights.

The second is horribly broken. It goes inside quoted data
and arguments to macros destroying the data. For example:

CL-USER> (macroexpand-1 '(with-gensyms (a b)
                          (let ((a '(somedata b c))
                                (b '(moredata c b a)))
                            (do stuff with a))))

(LET ((#:A1599 '(SOMEDATA #:B1600 C)) 
      (#:B1600 '(MOREDATA C #:B1600 #:A1599)))
  (DO STUFF WITH #:A1599))

Using backquote and comma seems unavoidable because you are
trying to get to this situation:

CL-USER> (macroexpand-1 '(with-gensyms (a b)
                          `(let ((,a '(somedata b c))
                                 (,b '(moredata c b a)))
                             (do stuff with ,a))))

(LET ((A (GENSYM)) (B (GENSYM)))
  `(LET ((,A '(SOMEDATA B C)) (,B '(MOREDATA C B A)))
     (DO STUFF WITH ,A)))
T

CL-USER> (eval *)

(LET ((#:G1605 '(SOMEDATA B C)) (#:G1606 '(MOREDATA C B A)))
  (DO STUFF WITH #:G1605))

Between macroexpansion time and run time some A's have been
replaced by gensyms, while other A's have been left, their
time is yet to come. CL needs the programmer to be explicit
about when things are done, whether at macroexpansion time
or at run time.

Alan Crowe
Edinburgh
Scotland