From: Ashrentum
Subject: Newbie doubt with macros ` , ,@
Date: 
Message-ID: <060a99e5-113a-489e-ab9c-04dd91163d33@i7g2000prf.googlegroups.com>
Hi. It is a newbie question about how parameters and vars are treated
inside macros and how to deal with the commas, back quotes, and commas-
at. I give this silly example:

(defmacro mymacro ()
  (let ((name (gensym))
    `(list ,(dotimes (,name 10 ,name) (print ,name))))) ;1-error

That is, I want, for whatever reasons, that the index variable for the
dotimes was the name generated by (gensym).
It is not possible to write commas inside a comma, so how can I get
inside of dotimes the name generated by (gensym)?

Of course the fix here would just be remove the comma before the
dotimes and it would be done.  But that would generate a code with an
inner macro and later would expand it, therefore I wouldn't see the
prints. I want to make the first, i.e., run the the inner macro before
the outer macro and see the prints.

How can you call first an internal macro with parameters of the outer
macro?



My scenario is this:

I had to define in one supermacro many bindings that were exactly the
same, i.e. different variables but with the same value. So for this I
thought that could be nice define another macro that got the value in
one parameter and a list of variables to bind with:

(defmacro let-samevars (definition &rest vars)
  (let ((defs))
    (dolist (var vars)
      (push (list var definition) defs))
    `',(reverse defs)))

So a call like this: (let-samevars nil v1 v2 v3) would create: '((v1
nil) (v2 nil) (v3 nil)). Of course, this makes more sense if instead
of nil is a complex expression.

So, let's pretend the supermacro is something like:

(defmacro supermacro ()
  (let* ((name (gensym)))
     `(let ((,name 5)
	     ,@(let-samevars name v1 v2))    ;2-error
         (list ,name v1 v2))))

That is, I want to creat a let like (let's say (gensym) created #:g01)
(let* ((#:g01 5) (v1 #:g01) (v2 #:g01))
  (list #:g01 v1 v2))

that hopefully would be (list 5 5 5)

But of course is incorrect because name in 2-error is 'name and not
#:g01. But here is mandatory to run first the inner macro. So how name
in the call to let-samevars can be #:g01 or the value generated by
(gensym)?

Is there a combination of commas that I don't know?

From: Thomas A. Russ
Subject: Re: Newbie doubt with macros ` , ,@
Date: 
Message-ID: <ymiejaq6y3m.fsf@blackcat.isi.edu>
Ashrentum <·········@gmail.com> writes:

> Hi. It is a newbie question about how parameters and vars are treated
> inside macros and how to deal with the commas, back quotes, and commas-
> at. I give this silly example:
> 
> (defmacro mymacro ()
>   (let ((name (gensym))
>     `(list ,(dotimes (,name 10 ,name) (print ,name))))) ;1-error
> 
> That is, I want, for whatever reasons, that the index variable for the
> dotimes was the name generated by (gensym).
> It is not possible to write commas inside a comma, so how can I get
> inside of dotimes the name generated by (gensym)?

Well, the issue here isn't so much the use of commas but issues of what
does and does not get evaluated when you pass the values.  It would be
possible to solve some of this using EVAL, but that is generally not
recommended.

Instead, it often makes sense to think carefully about what should be a
macro and what should be a function.

> How can you call first an internal macro with parameters of the outer
> macro?

You might not want the internal item to be a macro.  It is quite
legitimate and common to have helper FUNCTIONs that build part of the
code that a macro uses to generate an expansion.

> My scenario is this:
> 
> I had to define in one supermacro many bindings that were exactly the
> same, i.e. different variables but with the same value. So for this I
> thought that could be nice define another macro that got the value in
> one parameter and a list of variables to bind with:
> 
> (defmacro let-samevars (definition &rest vars)
>   (let ((defs))
>     (dolist (var vars)
>       (push (list var definition) defs))
>     `',(reverse defs)))

Well, consider doing this as a function, since the effect of using a
macro is that DEFINITION doesn't get evaluated.  And below, you want it
to get evaluated.

(defun distribute-value (vars value)
  ;; Creates a list where each element of VARS is paired with VALUE
  (let ((defs))
     (dolist (var vars (reverse defs))
       (push (list var value) defs))))

Note:  Important warning below.


> So a call like this: (let-samevars nil v1 v2 v3) would create: '((v1
> nil) (v2 nil) (v3 nil)). Of course, this makes more sense if instead
> of nil is a complex expression.
> 
> So, let's pretend the supermacro is something like:
> 
> (defmacro supermacro ()
>   (let* ((name (gensym)))
>      `(let ((,name 5)
> 	     ,@(let-samevars name v1 v2))    ;2-error
>          (list ,name v1 v2))))

This then becomes:

(defmacro supermacro ()
  (let* ((name (gensym)))
      `(let ((,name 5)
 	     ,@(distribute-value '(v1 v2) name)) ;; CHANGED
          (list ,name v1 v2))))

If having to quote the list of variables annoys you, you can wrap a
macro around that part, perhaps like

 (defmacro distribute ((&rest vars) value)
   `(distribute-value ',vars ,value))

and then you would invoke this macro like

   (distribute (v1 v2 v3) name)

and the variable list will not be evaluated (because it gets quoted in
the macro body, whereas name will be because it is passed unquoted
through to the underlying function, which will evaluate the argument.

> Is there a combination of commas that I don't know?

No, but there's a combination of macros and functions that you aren't
using.

Important Warning:

The only caveat is that you need to make sure that the DISTRIBUTE-VALUE
function is defined in the appropriate evaluation and compilation
environment where it is being used.  If you want to define a macro (like
I do below) that depends on DISTRIBUTE-VALUE and then use that macro in
the same file that both the macro and the function are defined, you will
need to wrap the function in an appropriate eval-when form:

(eval-when (:execute :compile-toplevel :load-toplevel)
  (defun distribute-value ...))



-- 
Thomas A. Russ,  USC/Information Sciences Institute
From: Ken Tilton
Subject: Re: Newbie doubt with macros ` , ,@
Date: 
Message-ID: <47cdaa17$0$25029$607ed4bc@cv.net>
Ashrentum wrote:
> Hi. It is a newbie question about how parameters and vars are treated
> inside macros and how to deal with the commas, back quotes, and commas-
> at. I give this silly example:
> 
> (defmacro mymacro ()
>   (let ((name (gensym))
>     `(list ,(dotimes (,name 10 ,name) (print ,name))))) ;1-error
> 
> That is, I want, for whatever reasons,...

Bzzt! You'll have to do better than "whatever" when asking for something 
that makes no sense.

I realize you may have distilled this from a real macro attempt, but the 
problem is back there in the real macro, not with "commas inside commas".

kenny

  that the index variable for the
> dotimes was the name generated by (gensym).
> It is not possible to write commas inside a comma, so how can I get
> inside of dotimes the name generated by (gensym)?
> 
> Of course the fix here would just be remove the comma before the
> dotimes and it would be done.  But that would generate a code with an
> inner macro and later would expand it, therefore I wouldn't see the
> prints. I want to make the first, i.e., run the the inner macro before
> the outer macro and see the prints.
> 
> How can you call first an internal macro with parameters of the outer
> macro?
> 
> 
> 
> My scenario is this:
> 
> I had to define in one supermacro many bindings that were exactly the
> same, i.e. different variables but with the same value. So for this I
> thought that could be nice define another macro that got the value in
> one parameter and a list of variables to bind with:
> 
> (defmacro let-samevars (definition &rest vars)
>   (let ((defs))
>     (dolist (var vars)
>       (push (list var definition) defs))
>     `',(reverse defs)))
> 
> So a call like this: (let-samevars nil v1 v2 v3) would create: '((v1
> nil) (v2 nil) (v3 nil)). Of course, this makes more sense if instead
> of nil is a complex expression.
> 
> So, let's pretend the supermacro is something like:
> 
> (defmacro supermacro ()
>   (let* ((name (gensym)))
>      `(let ((,name 5)
> 	     ,@(let-samevars name v1 v2))    ;2-error
>          (list ,name v1 v2))))
> 
> That is, I want to creat a let like (let's say (gensym) created #:g01)
> (let* ((#:g01 5) (v1 #:g01) (v2 #:g01))
>   (list #:g01 v1 v2))
> 
> that hopefully would be (list 5 5 5)
> 
> But of course is incorrect because name in 2-error is 'name and not
> #:g01. But here is mandatory to run first the inner macro. So how name
> in the call to let-samevars can be #:g01 or the value generated by
> (gensym)?
> 
> Is there a combination of commas that I don't know?

-- 
http://smuglispweeny.blogspot.com/
http://www.theoryyalgebra.com/

"In the morning, hear the Way;
  in the evening, die content!"
                     -- Confucius
From: Pascal J. Bourguignon
Subject: Re: Newbie doubt with macros ` , ,@
Date: 
Message-ID: <7cr6eqwi0d.fsf@pbourguignon.anevia.com>
Ashrentum <·········@gmail.com> writes:

> Hi. It is a newbie question about how parameters and vars are treated
> inside macros and how to deal with the commas, back quotes, and commas-
> at. I give this silly example:
>
> (defmacro mymacro ()
>   (let ((name (gensym))
>     `(list ,(dotimes (,name 10 ,name) (print ,name))))) ;1-error
>
> That is, I want, for whatever reasons, that the index variable for the
> dotimes was the name generated by (gensym).
> It is not possible to write commas inside a comma, so how can I get
> inside of dotimes the name generated by (gensym)?

By not writting a comma inside a comma.

                    ,( inside a comma )
                  `( inside a backquote )


         `(  ,( inside a comma inside a backquote ) )               good
 `(  ,(  ,( inside a comma inside a comma inside a backquote ) ) )  bad




> Of course the fix here would just be remove the comma before the
> dotimes and it would be done.  But that would generate a code with an
> inner macro and later would expand it, therefore I wouldn't see the
> prints. 

So you want to buid this expression:

   (list (dotimes (xyz 10 xyz) (print xyz)))

right?

Then just do it, remove that comma!

(defmacro mymacro ()
   (let ((name (gensym)))
     `(list (dotimes (,name 10 ,name) (print ,name)))))

(macroexpand '(mymacro))
--> (LIST (DOTIMES (#:G23900 10 #:G23900) (PRINT #:G23900))) ;
    T

See your PRINT alright?


I want to make the first, i.e., run the the inner macro before
> the outer macro and see the prints.


> How can you call first an internal macro with parameters of the outer
> macro?

By using MACROEXPAND or MACROEXPAND-1 explicitely:

(defmacro my-macro (p &environment environment)
  (let* ((name (gensym))
         (expansion (macroexpand `(dotimes (,name ,p ,name) (print ,name)))))
    ;; now what?  Do you know what you want to do with the expansion?
    ;; And don't you worry, while the root of that sexp won't be a DOTIMES,
    ;; it will probably contain some other inner macros!  You'd have to 
    ;; look for a code walker here. (eg #+clisp EXT:EXPAND-FORM).
    (format t "At macroexpansion time, let's see that PRINT: ~S~%" expansion)
    `(list ,expansion)))


 (macroexpand-1 '(my-macro p))

At macroexpansion time, let's see that PRINT: 
(BLOCK NIL
 (LET ((#:G23901 0) (#:G23902 P))
  (TAGBODY #:G23903 (IF (>= #:G23901 #:G23902) (GO #:G23904)) (PRINT #:G23901) (PSETQ #:G23901 (1+ #:G23901)) (GO #:G23903) #:G23904
   (RETURN-FROM NIL (PROGN #:G23901)))))

--> (LIST
      (BLOCK NIL
       (LET ((#:G23901 0) (#:G23902 P))
        (TAGBODY #:G23903 (IF (>= #:G23901 #:G23902) (GO #:G23904)) (PRINT #:G23901) (PSETQ #:G23901 (1+ #:G23901)) (GO #:G23903) #:G23904
         (RETURN-FROM NIL (PROGN #:G23901)))))) ;
     T

(let ((p 10)) (my-macro p))
At macroexpansion time, let's see that PRINT: 
(BLOCK NIL
 (LET ((#:G23909 0) (#:G23910 P))
  (TAGBODY #:G23911 (IF (>= #:G23909 #:G23910) (GO #:G23912)) (PRINT #:G23909) (PSETQ #:G23909 (1+ #:G23909)) (GO #:G23911) #:G23912
   (RETURN-FROM NIL (PROGN #:G23909)))))

0 
1 
2 
3 
4 
5 
6 
7 
8 
9 
--> (10)


> My scenario is this:
>
> I had to define in one supermacro many bindings that were exactly the
> same, i.e. different variables but with the same value. So for this I
> thought that could be nice define another macro that got the value in
> one parameter and a list of variables to bind with:
>
> (defmacro let-samevars (definition &rest vars)
>   (let ((defs))
>     (dolist (var vars)
>       (push (list var definition) defs))
>     `',(reverse defs)))
>
> So a call like this: (let-samevars nil v1 v2 v3) would create: '((v1
> nil) (v2 nil) (v3 nil)). Of course, this makes more sense if instead
> of nil is a complex expression.

And do you want to compute this complex expression it once or N times?

If you want to compute the value once per variable:

(defmacro let-samevars ((value &rest vars) &body body)
  `(let ,(mapcar (lambda (var) (list var value)) vars) ,@body))

If you want to compute it only once:

(defmacro let-samevars ((value &rest vars) &body body)
  (let ((vvalue (gensym)))
    `(let ((,vvalue ,value))
        (let ,(mapcar (lambda (var) (list var vvalue)) vars) ,@body))))

                   
> So, let's pretend the supermacro is something like:
>
> (defmacro supermacro ()
>   (let* ((name (gensym)))
>      `(let ((,name 5)
> 	     ,@(let-samevars name v1 v2))    ;2-error
>          (list ,name v1 v2))))

 (defmacro supermacro ()
   (let* ((name (gensym)))
      `(let ((,name 5))
 	     (let-samevars (,name v1 v2)
            (list ,name v1 v2)))))

(ext:expand-form '(supermacro))
--> (LET ((#:G23924 5)) (LET ((V1 #:G23924) (V2 #:G23924)) (LIST #:G23924 V1 V2))) ;
    T
(supermacro)
--> (5 5 5)

> that hopefully would be (list 5 5 5)

No, see above.


> But of course is incorrect because name in 2-error is 'name and not
> #:g01. But here is mandatory to run first the inner macro. 

Why?  To be able to expand an inner macro first, you'd have to know
before hand what it is.  And this could done only at macro expansion
time.

> So how name
> in the call to let-samevars can be #:g01 or the value generated by
> (gensym)?
>
> Is there a combination of commas that I don't know?

The key to becoming expert in macro writting is NOT to write any
macro.  Just write functions that take forms and return forms.  Once
you can write these functions, you'll know how to write macros.

The key to becoming expert in use of backquote and comma is NOT to use
them.  Just write expressions that build forms using LIST, CONS,
APPEND, etc.  Once you know how to build such expressions (and
possibly how to build expressions that build such expressions) using
only normal list functions, you'll know how to use backquote and comma.


Once you'll know how to write macros, and how to use backquote and
comma, you'll be able to use backquote and comma in your macros.

-- 
__Pascal Bourguignon__