From: Vladimir Zolotykh
Subject: defmacro & eval
Date: 
Message-ID: <3BEC27A8.D670AC9B@eurocom.od.ua>
Hi

Consider the following simple macro:

  (defmacro foo (a)
    `(list ,@(loop for (s nil c) in a
                 collecting `((:option :value ,s) 
                              ,c))))
The following usage of it works as expected:

  (foo (("a" :a "A") ("b" :b "B") ("c" :c "C")))

But suppose I'll define some variable, because I'm going to
use the same value several times:

  (defvar *bar* '(("a" :a "A") ("b" :b "B") ("c" :c "C")))

And then try to call FOO the following way:

  (foo *bar*)

This doesn't work. I can surround A inside FOO with EVAL. But
I've read somewhere that this would be a bad style. I think
some technique exists for such things, please let me know.

Vladimir

-- 
Vladimir Zolotykh                         ······@eurocom.od.ua

From: Kenny Tilton
Subject: Re: defmacro & eval
Date: 
Message-ID: <3BEC3A91.7E8CC996@nyc.rr.com>
Vladimir Zolotykh wrote:
> 
> Hi
> 
> Consider the following simple macro:
> 
>   (defmacro foo (a)
>     `(list ,@(loop for (s nil c) in a
>                  collecting `((:option :value ,s)
>                               ,c))))
> The following usage of it works as expected:
> 
>   (foo (("a" :a "A") ("b" :b "B") ("c" :c "C")))

It does? In ACL I get the expansion:


  (LIST ((:OPTION :VALUE "a") "A") ((:OPTION :VALUE "b") "B") ((:OPTION
:VALUE "c") "C"))

and the error:

  Error: Illegal function object: (:OPTION :VALUE "a").

I would offer a fix but you said it worked, i am not sure what output
you want, and I am not sure this has to be a macro.

Otherwise I am stumped by the real problem, that the loop variable a
albeit unquoted is not being evaluated. I wager this has something to do
with the LOOP macro, on which I am ignorant (and wish to stay that way
<g>).

kenny
clinisys
From: Kaz Kylheku
Subject: Re: defmacro & eval
Date: 
Message-ID: <u4XG7.30506$Ud.1233225@news1.rdc1.bc.home.com>
In article <·················@eurocom.od.ua>, Vladimir Zolotykh wrote:
>Hi
>
>Consider the following simple macro:
>
>  (defmacro foo (a)
>    `(list ,@(loop for (s nil c) in a
>                 collecting `((:option :value ,s) 
>                              ,c))))
>The following usage of it works as expected:
>
>  (foo (("a" :a "A") ("b" :b "B") ("c" :c "C")))

It doesn't work for me. It expands to

   (LIST ((:OPTION :VALUE "a") "A) ( ... ) ...)

This blows up because (:OPTION :VALUE "a") is not a function. What
Lisp implementation are you using?

>But suppose I'll define some variable, because I'm going to
>use the same value several times:
>
>  (defvar *bar* '(("a" :a "A") ("b" :b "B") ("c" :c "C")))
>
>And then try to call FOO the following way:
>
>  (foo *bar*)
>
>This doesn't work.

The problem with this approach is that the macro performs the loop
expansion at macro-evaluation time. This is fine if it's a top-level form.
But suppose it's inside some context where it can be evaluated more
than once:

	(defun xyzzy ()
	  (foo *bar*))

Each time you call xyzzy, it will return the cooked answer based on
evaluating the original macro-expansion of (foo *bar*). That is, if
you change the value of *bar* and call xyzzy again, the result won't
change to reflect that.

I think you want the loop construct itself, rather than its result,
to be part of the expanded form.

>I can surround A inside FOO with EVAL. But

How can that work? A is not an evaluable form. eval is going to
complain that ("a" :a "a") is not a function. Try it:

	(eval '(("a" :a "A") ("b" :b "B")))

>I've read somewhere that this would be a bad style. I think

Perhaps in the FAQ. In many cases when you think you need eval, it
turns out that you don't, in particular if you are in a macro.

What you want is to arrange the macro expansion so that whatever needs
to be evaluated each time the expansion is evaluated is part of that
expansion, and is thereby simply thrown into the path of the evaluator,
so to speak.

The macroexpand function and macroexpand-1 functions are your friend;
use it to investigate what the macro-expansion looks like, keeping in
mind that the macro is replaced by that expansion but once.

Try this instead:

(defmacro foo (a)
 `(loop for (s nil c) in ,a
    collecting `((:option :value ,s) ,c)))

Then call it like this (note the quoting):

(foo '(("a" :a "A") ("b" :b "B") ("c" :c "C")))

If you want to get rid of the quoting, that will take more work, because
then your macro has to decide whether the argument needs to be evaluated.
An symbol argument like *bar* will have to be evaluated to produce
the corresponding list. But a list like (("a" :a "A")) won't be evaluated.
Thirdly, the macro will also have to handle the case when the argument
is something like (function-returning-list some-param); in that case
it will have to evaluate. So the macro will have to have a conditional
form to handle all these cases.

The way it is done above is simpler because the argument is simply
assumed to be a form that evaluates to a list, and so no conditional
logic is needed.
From: Gabe Garza
Subject: Re: defmacro & eval
Date: 
Message-ID: <ofmbfyt6.fsf@kynopolis.org>
Vladimir Zolotykh <······@eurocom.od.ua> writes:

> Consider the following simple macro:
> 
>   (defmacro foo (a)
>     `(list ,@(loop for (s nil c) in a
>                  collecting `((:option :value ,s) 
>                               ,c))))

   Why do you wish to right this has a macro?  It isn't a syntax
transformation and can easily be written as a function:

(defun foo (a)
  (loop for (s nil c) in a
     collecting `((:option :value ,s) ,c)))

If you're concerned about efficiency, you can declare it inline
by inserting:

(declaim (inline foo))

immediately before the function definition.

> The following usage of it works as expected:
> 
>   (foo (("a" :a "A") ("b" :b "B") ("c" :c "C")))
> 
> But suppose I'll define some variable, because I'm going to
> use the same value several times:
> 
>   (defvar *bar* '(("a" :a "A") ("b" :b "B") ("c" :c "C")))
> 
> And then try to call FOO the following way:
> 
>   (foo *bar*)

   Because you have defined FOO to be a macro and not a function,
its arguments are not evaluated, thus, the macro does not get
passed the list that is *BAR*'s value.  Instead, it gets passed
the symbol *BAR*.   Obviously, iterating over the elements of a
symbol don't make sense. :)  If you need to access the value of
a symbol in a macro, you can use SYMBOL-VALUE.  But in this case
you shouldn't be using a macro.

   If this doesn't make sense, try put code to print the arguments 
in the macros definition, then call it with both examples a few times.

   Note that if you rewrite it has a function, you'll have to quote
the argument (i.e., you'll have to write 

(foo '(("a" :a "A") ("b" :b "B")))

instead of 

(foo (("a" :a "A") ("b" :b "B")))


Gabe Garza
From: Coby Beck
Subject: Re: defmacro & eval
Date: 
Message-ID: <7PXG7.48480$zK1.12758952@typhoon.tampabay.rr.com>
"Vladimir Zolotykh" <······@eurocom.od.ua> wrote in message
······················@eurocom.od.ua...
> Hi
>
> Consider the following simple macro:
>
>   (defmacro foo (a)
>     `(list ,@(loop for (s nil c) in a
>                  collecting `((:option :value ,s)
>                               ,c))))
> The following usage of it works as expected:
>
>   (foo (("a" :a "A") ("b" :b "B") ("c" :c "C")))
>
> But suppose I'll define some variable, because I'm going to
> use the same value several times:
>
>   (defvar *bar* '(("a" :a "A") ("b" :b "B") ("c" :c "C")))
>
> And then try to call FOO the following way:
>
>   (foo *bar*)
>

Perhaps foo should be a function?  What is it being used for?

Coby
--
(remove #\space "coby . beck @ opentechgroup . com")
From: Dr. Edmund Weitz
Subject: Re: defmacro & eval
Date: 
Message-ID: <m3itcj3bpo.fsf@bird.agharta.de>
Vladimir Zolotykh <······@eurocom.od.ua> writes:

> Hi
> 
> Consider the following simple macro:
> 
>   (defmacro foo (a)
>     `(list ,@(loop for (s nil c) in a
>                  collecting `((:option :value ,s) 
>                               ,c))))
> The following usage of it works as expected:
> 
>   (foo (("a" :a "A") ("b" :b "B") ("c" :c "C")))
> 
> But suppose I'll define some variable, because I'm going to
> use the same value several times:
> 
>   (defvar *bar* '(("a" :a "A") ("b" :b "B") ("c" :c "C")))
> 
> And then try to call FOO the following way:
> 
>   (foo *bar*)
> 
> This doesn't work. I can surround A inside FOO with EVAL. But
> I've read somewhere that this would be a bad style. I think
> some technique exists for such things, please let me know.
> 
> Vladimir
> 
> -- 
> Vladimir Zolotykh                         ······@eurocom.od.ua

Hmm,

it looks to me as if you want to transform a list and I wonder why you
need a macro at all. Wouldn't a function like this be sufficient?

  * (defun foo (a)
     (loop for (s nil c) in a
           collecting `((:option :value ,s) 
                        ,c)))
  FOO
  * (foo '(("a" :a "A") ("b" :b "B") ("c" :c "C")))
  (((:OPTION :VALUE "a") "A") ((:OPTION :VALUE "b") "B")
   ((:OPTION :VALUE "c") "C"))
  * (defparameter *bar* '(("a" :a "A") ("b" :b "B") ("c" :c "C")))
  *BAR*
  * (foo *bar*)
  (((:OPTION :VALUE "a") "A") ((:OPTION :VALUE "b") "B")
   ((:OPTION :VALUE "c") "C"))

As far as evaluating of macro parameters is concerned - I had the same
problem a while ago. The thread can be found at

  <http://groups.google.com/groups?hl=en&th=76e1c9d205b1bd41>

The general technique is to evaluate the arguments you want to be
evaluated once and bind them to gensyms before you start the actual
macro-expansion code (inside of which you use the gensyms).

Hope that helps,
Edi.

PS: As Kenny Tilton has pointed out, your example above won't work if
you evaluate it in the REPL but maybe you use it somewhere else.
From: Vladimir Zolotykh
Subject: Re: defmacro & eval
Date: 
Message-ID: <3BED5A10.9359F497@eurocom.od.ua>
Thank you all.

I carefully read your answers. Sorry for rather inaccurate question.
The code from which question raised follows:

(defmethod new-order ((order (eql :package)) username &rest keys)
  (declare (ignore keys))
  username
  (macrolet 
      ((my-select (a)
         `(html
           ,@(loop for (actual . visible) in (eval a)
                 collecting `((:option :value ,actual) 
                              ,visible)))))
    (html
     ;; [Some unrelated stuff skipped]
     ((:form :action "/new-order"
             :type "post")
      ((:select :name "package")
       (my-select +packages+))
      :br
      ((:input :type "submit" :value ""))))))

Using Allegro Serve and HTML Generation Facility.

Were +packages+ defined as:

(eval-when (:compile-toplevel :load-toplevel :execute)
(defconstant +packages+
    '(("pack1" . "Package One")
      ("pack2" . "Package Two")
      ("pack3" . "Package There"))
)

This above works but I dislike this approach and still
don't know better one.

-- 
Vladimir Zolotykh                         ······@eurocom.od.ua
From: Vladimir Zolotykh
Subject: Re: defmacro & eval
Date: 
Message-ID: <3BEE3C25.1CC302F4@eurocom.od.ua>
Let me to emphasize. I don't see how I can implement my-select
as a function or as a macro that expands to LOOP rather that
to its result.

-- 
Vladimir Zolotykh                         ······@eurocom.od.ua
From: Vladimir Zolotykh
Subject: Re: defmacro & eval
Date: 
Message-ID: <3BEE97B3.C4F94A1F@eurocom.od.ua>
Found it. My mistake. Normal function do the job.
Here is the solution.

(defun test-select-options ()
    (flet ((select-options-not-work (options)
             (loop for (value . text) in options
                 do (html ((:option :value value) (:princ text))))))
      (html-stream
       *standard-output*
       (html
            (:html
             (:head (:title "Select Options"))
             (:body
              ((:form :action "/select-options"
                      :method "post")
               ((:select :name "select")
                (select-options-not-work '(("1" . "One")
                                  ("2" . "Two"))))
               :br
               ((:input :type "submit"))))))))))

Just forgot about :princ.

-- 
Vladimir Zolotykh                         ······@eurocom.od.ua