From: Jordan Katz
Subject: writing the 'for' macro.
Date: 
Message-ID: <m3n0vncjky.fsf@underlevel.underlevel.net>
Hi,

  I tried writing the for macro, which is supposed to work like this:

  (for x 1 3 (format t "~A " x)) => 1 2 3

  This is what I got:

  (defmacro for (x start end body)
    `(do ((x ,start (1+ x)))
      ((> x ,end)) (,@body)))

  This works (sort of) but the (,@body) notation seems wrong to me.  I
  haven't seen the macros in my book (Graham's ANSI CL) use it at
  all.  Most just use ,@body as the body to a do call.  What is the
  correct way to write this?  Have I overlooked a possible usage of a
  gensym?

  Other than this specific case where I'm questioning my solution, I'm
  generally confused about when to use gensyms.  Where could I find a
  documents about macros that perhaps outlines when gensyms should be
  used?

Thanks a lot,
-- 
Jordan Katz <····@underlevel.net>  |  Mind the gap

From: Pierpaolo BERNARDI
Subject: Re: writing the 'for' macro.
Date: 
Message-ID: <uX3z8.74710$vF6.2236465@news2.tin.it>
"Jordan Katz" <····@underlevel.net> ha scritto nel messaggio ···················@underlevel.underlevel.net...
> Hi,
>
>   I tried writing the for macro, which is supposed to work like this:
>
>   (for x 1 3 (format t "~A " x)) => 1 2 3
>
>   This is what I got:
>
>   (defmacro for (x start end body)
>     `(do ((x ,start (1+ x)))
>       ((> x ,end)) (,@body)))

(,@BODY)  is the same thing as BODY.

>   This works (sort of) but the (,@body) notation seems wrong to me.  I
>   haven't seen the macros in my book (Graham's ANSI CL) use it at
>   all.  Most just use ,@body as the body to a do call.  What is the
>   correct way to write this?  Have I overlooked a possible usage of a
>   gensym?

In your macro, the body must be a single form. Is this
what you want?

I'd say, instead:

(defmacro for (x start end &body body)
  `(do ((x ,start (1+ x)))
       ((> x ,end))
     ,@body))



>   Other than this specific case where I'm questioning my solution, I'm
>   generally confused about when to use gensyms.

Use them when you are introducing identifiers in the
expansion that you want to be transparent (there aren't
in this case).

> Where could I find a
>   documents about macros that perhaps outlines when gensyms should be
>   used?

On Lisp.


P.
From: Alexey Dejneka
Subject: Re: writing the 'for' macro.
Date: 
Message-ID: <m3it6bduaw.fsf@comail.ru>
Hello,

Jordan Katz <····@underlevel.net> writes:

>   I tried writing the for macro, which is supposed to work like this:
> 
>   (for x 1 3 (format t "~A " x)) => 1 2 3
> 
>   This is what I got:
> 
>   (defmacro for (x start end body)
>     `(do ((x ,start (1+ x)))
>       ((> x ,end)) (,@body)))
> 
>   This works (sort of) but the (,@body) notation seems wrong to me.

1. What implementation if CL are you using? I've tried to enter your
   definition in SBCL and obtained:

----
* (defmacro for (x start end body)
    `(do ((x ,start (1+ x)))
         ((> x ,end)) (,@body)))
; in: LAMBDA NIL
;     (LET* ((X (CAR #)) (START (CAR #)) (END (CAR #)) (BODY (CAR #)))
;     `(DO ((X ,START #)) ((> X ,END)) ,BODY))
; 
; caught STYLE-WARNING:
;   The variable X is defined but never used.
; compilation unit finished
;   caught 1 STYLE-WARNING condition
----

   And you see your code is not ok: your macro receives a name for a
   loop variable, but always uses a fixed name X:

----
* (for x 1 3 (print x))
1 
2 
3 
NIL
* (for y 1 3 (print y))

debugger invoked on condition of type UNBOUND-VARIABLE:
  The variable Y is unbound.
----

   So the first correction is to replace X with ,X:

   (defmacro for (x start end body)
     `(do ((,x ,start (1+ ,x)))
          ((> ,x ,end)) (,@body)))

2. What is a ``body''? In your macro it must be exactly 1 form, which
   you want to place in the DO body:

   (defmacro for (x start end body)
     `(do ((,x ,start (1+ ,x)))
          ((> ,x ,end)) ,body))

 Usually a body is a list of forms, so it is more convenient to write:

   (defmacro for (x start end &body body)
     `(do ((,x ,start (1+ ,x)))
          ((> ,x ,end)) ,@body))

3. Your macro reevaluates END form on each iteration:

----
* (let ((n 10))
    (for i 1 (decf n) (print i)))
1 
2 
3 
4 
5 
NIL
----

   The loop body is executed 5 times, but should be 9. So you need to
   store a value of END in some variable outside the loop:

   (defmacro for (x start end &body body)
     `(let ((end-value ,end))
        (do ((,x ,start (1+ ,x)))
            ((> ,x end-value)) ,@body)))

   But now the code generated by this macro binds a variable
   END-VALUE, which may be used in user code:

----
* (let ((end-value 1))
    (for i 1 5 do (print (+ i end-value))))
6 
7 
8 
9 
10 
NIL
----

   One way is to use a freshly generated symbol:

   (defmacro for (x start end &body body)
     (let ((end-name (gensym "END")))
       `(let ((,end-name ,end))
          (do ((,x ,start (1+ ,x)))
              ((> ,x ,end-name)) ,@body))))

>   Where could I find a documents about macros that perhaps outlines
>   when gensyms should be used?

"On Lisp": www.paulgraham.com/onlisp.html

Regards,
Alexey Dejneka

---
"You've said you removed a bug. Why has the code grown?"
From: Alexey Dejneka
Subject: Re: writing the 'for' macro.
Date: 
Message-ID: <m3vgabceia.fsf@comail.ru>
Alexey Dejneka <········@comail.ru> writes:

>    (defmacro for (x start end &body body)
>      (let ((end-name (gensym "END")))
>        `(let ((,end-name ,end))
>           (do ((,x ,start (1+ ,x)))
>               ((> ,x ,end-name)) ,@body))))

This code has a stylistic problem. Usually in CL forms are evaluated
left-to-right, but this macro evaluates END before START:

----
* (let ((n 10))
    (for x (decf n 5) (incf n 6) (print x)))
11 
12 
13 
14 
15 
16 
NIL
----

But the lower bound should be 5, upper -- 11.

(defmacro for (x start end &body body)
  (let ((start-name (gensym "START"))
        (end-name (gensym "END")))
    `(let ((,start-name ,start)
           (,end-name ,end))
      (do ((,x ,start-name (1+ ,x)))
           ((> ,x ,end-name)) ,@body))))

Regards,
Alexey Dejneka

---
"You've said you removed a bug. Why has the code grown?"
From: Kent M Pitman
Subject: Re: writing the 'for' macro.
Date: 
Message-ID: <sfw7kmrymn2.fsf@shell01.TheWorld.com>
Alexey Dejneka <········@comail.ru> writes:

> Alexey Dejneka <········@comail.ru> writes:
> 
> >    (defmacro for (x start end &body body)
> >      (let ((end-name (gensym "END")))
> >        `(let ((,end-name ,end))
> >           (do ((,x ,start (1+ ,x)))
> >               ((> ,x ,end-name)) ,@body))))
> 
> This code has a stylistic problem. Usually in CL forms are evaluated
> left-to-right, but this macro evaluates END before START: [...]

(defmacro for (x start end &body body)
  `(loop for ,x from ,start to ,end do (progn ,@body)))

or use TAGBODY instead of PROGN, depending on your intended treatment of
toplevel atoms.

LOOP already manages ordering issues.
From: Frode Vatvedt Fjeld
Subject: Re: writing the 'for' macro.
Date: 
Message-ID: <2h8z76udej.fsf@vserver.cs.uit.no>
Jordan Katz <····@underlevel.net> writes:

>   I tried writing the for macro, which is supposed to work like
>   this:
>
>   (for x 1 3 (format t "~A " x)) => 1 2 3

I think this would be more consistent with other lisp syntax:

  (for (x 1 3) (format t ..))

Also, your notation above would usually indicate that the for form
returns three values: 1, 2, and 3. I understand you mean those values
are printed, but what should be returned?

You could follow for example dolist's lead:

  (for (x 1 3 'a-value) (print x))

1
2
3
=> A-VALUE

This also shows why it's a good idea to have those parens around the
control parameters: You get a place to put &optionals and &keys.

>   (defmacro for (x start end body)
>     `(do ((x ,start (1+ x)))
>       ((> x ,end)) (,@body)))

"(,@body)" is equivalent to ",body". But what you're looking for here
is ",@body". My proposed syntax could be implemented something like this:

  (defmacro for ((x start-form end-form &optional result-form) &body body)
    `(do ((,x ,start-form (1+ ,x))
          ((end ,end-form)))
         ((> ,x end) ,result-form)
        ,@body))

[Notice you want ",x" rather than just "x", too.]

> Other than this specific case where I'm questioning my solution, I'm
> generally confused about when to use gensyms.  Where could I find a
> documents about macros that perhaps outlines when gensyms should be
> used?

Consider my macro above, and what would happen in this case:

  (let (end)
    (for (i 0 (compute-a-value))
      (let ((j (confuzerize i)))
        (print j)
        (setf end j)))
    (format t "This is the end: ~W" end)
    end)

The intention of this code should be clear, but it won't work. If you
don't immediately see why, try to macroexpand the code and check the
lexical binding of end at various points.

This is the problem that would be solved by using a gensym rather than
simply the symbol end in my macro, and I consider this a bug in this
implementation of it.

Finally, for such a macro to be really useful, I'd say you should deal
with things like downwards stepping, variable-sized steps, and.. oh
wait, loop already does all of that :-)

-- 
Frode Vatvedt Fjeld
From: Thomas A. Russ
Subject: Re: writing the 'for' macro.
Date: 
Message-ID: <ymiu1pqaxm2.fsf@sevak.isi.edu>
Jordan Katz <····@underlevel.net> writes:

> 
> Hi,
> 
>   I tried writing the for macro, which is supposed to work like this:
> 
>   (for x 1 3 (format t "~A " x)) => 1 2 3
> 
>   This is what I got:
> 
>   (defmacro for (x start end body)
>     `(do ((x ,start (1+ x)))
>       ((> x ,end)) (,@body)))

But what do you get for this input:

   (for y 1 3 (format t "~A " y))

>   This works (sort of) but the (,@body) notation seems wrong to me.

Hmmm.  I'm a bit surprised that it works.

One thing that is very helpful when trying to write and especially debug
macros is to use the function MACROEXPAND and MACROEXPAND-1.  This will
cause expansion of macros (or one level of expansion) and return the
result.  Often a good way to look at it is to do something like

  (pprint (macroexpand-1 '(for y 1 3 (format t "~A " y))))

to get a nice printed representation of what the macro returns.  You can
then examine this code and make sure that it looks like what you intend.

The way I often write simple macros is to start with the code that I
want to produce and then work backwards from there, using backquote.  I
think this is like what you have done.

To get a good handle on the ,@ issue, you will need a test case that has
more than one form to execute in the body, for example:

   (for y 1 3 (format t "~A " y) (format t " ~A" (* y y)))

[Yes, I know that can fit in one format statement, but the point is to
 have multiple forms in the body.]

>   Other than this specific case where I'm questioning my solution, I'm
>   generally confused about when to use gensyms.  Where could I find a
>   documents about macros that perhaps outlines when gensyms should be
>   used?

Gensyms should generally be used anytime you introduce a variable in the
Macro and put any user provided code inside that binding.  In other
words, if your macro might include something like:

    `(let ((x ...))
        ...
        ,@body)

then you would need to worry about variable capture.  For a silly
example, consider:

(defmacro silly (&body body)
  `(let ((y 3))
     ,@body
     (format t "~%Macro: Y is ~A" y)))

and then look at the difference between the following forms:

(let ((x 10))
 (silly (setq x 20))
 (format t "~%Let:   X is ~A" x))

(let ((y 10))
 (silly (setq y 20))
 (format t "~%Let:   Y is ~A" y))



-- 
Thomas A. Russ,  USC/Information Sciences Institute          ···@isi.edu