From: ·····················@gmail.com
Subject: macro that writes macros
Date: 
Message-ID: <915225bf-8cc6-465b-8df5-f7ebc4cbd0e7@e23g2000prf.googlegroups.com>
Hi,

I'm learning lisp and tried to make this little program to write a web
page:


(defmacro new-tag (name)
  (let ((sname (string-downcase (symbol-name name))))
    `(defmacro ,name (&rest content)
       `(progn
	  (format t "<~a>~%" ,',sname)
	  ,@content
	  (format t "</~a>~%" ,',sname)))))

(new-tag html)
(new-tag head)
(new-tag title)
(new-tag body)
(new-tag p)

;; With that, now a web page can be "self-generated":
(html
 (head
  (title "My web page"))
 (body
  (p "Hello world")))


And it works. But then I thought about combining all those "(new-
tag ...)" like this:

(defmacro new-tags (&rest tags)
  (dolist (tag tags)
    `(new-tag ,tag)))

(new-tags html head title body p)

But it doesn't work! I tried without the "`" nor the "," in the "new-
tags" definition and it doesn't work either. I have been fighting with
this simple-looking problem for a long time, reading docs and stuff,
and I can't make it work anyway nor see what is wrong! Anyone has some
advice on what's going on or what I should check?

Thanks!
jordi

From: Dan Bensen
Subject: Re: macro that writes macros
Date: 
Message-ID: <fl71v3$oie$1@wildfire.prairienet.org>
·····················@gmail.com wrote:
 > (defmacro new-tags (&rest tags)
 >   (dolist (tag tags)
 >     `(new-tag ,tag)))

You're not doing anything with the new-tag forms.  You're
not even evaluating them, because they're quoted.  You're
just evaluating each backquote form, which returns an
unevaluated new-tag form, which is immediately thrown away.
Look up dolist in the hyperspec.  It returns nil by default,
so your macro isn't returning anything.

What you need to do is return the whole list of new-tag
forms to be evaluated.  Per Kenny the Great's answer to this
question, which I asked a few months ago, you need to wrap
the forms in a progn form so you can return all of them as
one form:

(defmacro new-tags (&rest tags)
   `(progn
     ,@(mapcar (lambda (tag) `(new-tag ,tag))
	     tags)))

* (new-tags foo bar)
* (foo (bar "stuff"))
<foo>
<bar>
</bar>
</foo>

Notice that regular text doesn't get printed.  If you
want to use a modern functional approach instead of cgi,
you might consider having your tag functions return a
string instead of printing it.  Then you can concatenate
each pair of start/end tags with the output of the enclosed
tags, and regular text will be included.  All you have to
do is output the final page once you've created it.

-- 
Dan
www.prairienet.org/~dsb/
From: Barry Margolin
Subject: Re: macro that writes macros
Date: 
Message-ID: <barmar-190A79.21444529122007@comcast.dca.giganews.com>
In article 
<····································@e23g2000prf.googlegroups.com>,
 ·····················@gmail.com wrote:

> Hi,
> 
> I'm learning lisp and tried to make this little program to write a web
> page:
> 
> 
> (defmacro new-tag (name)
>   (let ((sname (string-downcase (symbol-name name))))
>     `(defmacro ,name (&rest content)
>        `(progn
> 	  (format t "<~a>~%" ,',sname)
> 	  ,@content
> 	  (format t "</~a>~%" ,',sname)))))
> 
> (new-tag html)
> (new-tag head)
> (new-tag title)
> (new-tag body)
> (new-tag p)
> 
> ;; With that, now a web page can be "self-generated":
> (html
>  (head
>   (title "My web page"))
>  (body
>   (p "Hello world")))
> 
> 
> And it works. But then I thought about combining all those "(new-
> tag ...)" like this:
> 
> (defmacro new-tags (&rest tags)
>   (dolist (tag tags)
>     `(new-tag ,tag)))
> 
> (new-tags html head title body p)
> 
> But it doesn't work! I tried without the "`" nor the "," in the "new-
> tags" definition and it doesn't work either. I have been fighting with
> this simple-looking problem for a long time, reading docs and stuff,
> and I can't make it work anyway nor see what is wrong! Anyone has some
> advice on what's going on or what I should check?

Remember, what matters for a macro is what it RETURNS -- this is then 
executed in place of the macro call.  And your NEW-TAGS macro doesn't 
return anything.  The body of a DOLIST is executed just for its side 
effect, it's not accumulated into the result.  In DOLIST, you specify 
the return value as the third element of the first sub-form, and it 
defaults to NIL.

What you want is:

(defmacro new-tags (&rest tags)
  `(progn
     ,(loop for tag in tags
            collect `(new-tag ,tag))))

-- 
Barry Margolin, ······@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
*** PLEASE don't copy me on replies, I'll read them in the group ***
From: jordi
Subject: Re: macro that writes macros
Date: 
Message-ID: <71780390-11f0-4494-bbd4-09caa3194e73@i12g2000prf.googlegroups.com>
On Dec 30, 3:44 am, Barry Margolin <······@alum.mit.edu> wrote:
> In article
> <····································@e23g2000prf.googlegroups.com>,
>
>
>
>  ·····················@gmail.com wrote:
> > Hi,
>
> > I'm learning lisp and tried to make this little program to write a web
> > page:
>
> > (defmacro new-tag (name)
> >   (let ((sname (string-downcase (symbol-name name))))
> >     `(defmacro ,name (&rest content)
> >        `(progn
> >      (format t "<~a>~%" ,',sname)
> >      ,@content
> >      (format t "</~a>~%" ,',sname)))))
>
> > (new-tag html)
> > (new-tag head)
> > (new-tag title)
> > (new-tag body)
> > (new-tag p)
>
> > ;; With that, now a web page can be "self-generated":
> > (html
> >  (head
> >   (title "My web page"))
> >  (body
> >   (p "Hello world")))
>
> > And it works. But then I thought about combining all those "(new-
> > tag ...)" like this:
>
> > (defmacro new-tags (&rest tags)
> >   (dolist (tag tags)
> >     `(new-tag ,tag)))
>
> > (new-tags html head title body p)
>
> > But it doesn't work! I tried without the "`" nor the "," in the "new-
> > tags" definition and it doesn't work either. I have been fighting with
> > this simple-looking problem for a long time, reading docs and stuff,
> > and I can't make it work anyway nor see what is wrong! Anyone has some
> > advice on what's going on or what I should check?
>
> Remember, what matters for a macro is what it RETURNS -- this is then
> executed in place of the macro call.  And your NEW-TAGS macro doesn't
> return anything.  The body of a DOLIST is executed just for its side
> effect, it's not accumulated into the result.  In DOLIST, you specify
> the return value as the third element of the first sub-form, and it
> defaults to NIL.
>
> What you want is:
>
> (defmacro new-tags (&rest tags)
>   `(progn
>      ,(loop for tag in tags
>             collect `(new-tag ,tag))))
>

Indeed! Yes, I finally understand it, and by the way I am pretty happy
with being able to write in such a condensed way with lisp... it works
great. So many thanks, Barry, John and Dan!

jordi
From: ·············@gmail.com
Subject: Re: macro that writes macros
Date: 
Message-ID: <81ea05b6-7b23-4c88-b34a-b43b4615c132@j20g2000hsi.googlegroups.com>
On Dec 29 2007, 9:44 pm, Barry Margolin <······@alum.mit.edu> wrote:
> In article
> <····································@e23g2000prf.googlegroups.com>,
>
>
>
>  ·····················@gmail.com wrote:
> > Hi,
>
> > I'm learning lisp and tried to make this little program to write a web
> > page:
>
> > (defmacro new-tag (name)
> >   (let ((sname (string-downcase (symbol-name name))))
> >     `(defmacro ,name (&rest content)
> >        `(progn
> >      (format t "<~a>~%" ,',sname)
> >      ,@content
> >      (format t "</~a>~%" ,',sname)))))
>
> > (new-tag html)
> > (new-tag head)
> > (new-tag title)
> > (new-tag body)
> > (new-tag p)
>
> > ;; With that, now a web page can be "self-generated":
> > (html
> >  (head
> >   (title "My web page"))
> >  (body
> >   (p "Hello world")))
>
> > And it works. But then I thought about combining all those "(new-
> > tag ...)" like this:
>
> > (defmacro new-tags (&rest tags)
> >   (dolist (tag tags)
> >     `(new-tag ,tag)))
>
> > (new-tags html head title body p)
>
> > But it doesn't work! I tried without the "`" nor the "," in the "new-
> > tags" definition and it doesn't work either. I have been fighting with
> > this simple-looking problem for a long time, reading docs and stuff,
> > and I can't make it work anyway nor see what is wrong! Anyone has some
> > advice on what's going on or what I should check?
>
> Remember, what matters for a macro is what it RETURNS -- this is then
> executed in place of the macro call.  And your NEW-TAGS macro doesn't
> return anything.  The body of a DOLIST is executed just for its side
> effect, it's not accumulated into the result.  In DOLIST, you specify
> the return value as the third element of the first sub-form, and it
> defaults to NIL.
>
> What you want is:
>
> (defmacro new-tags (&rest tags)
>   `(progn
>      ,(loop for tag in tags
>             collect `(new-tag ,tag))))
>
> --
> Barry Margolin, ······@alum.mit.edu
> Arlington, MA
> *** PLEASE post questions in newsgroups, not directly to me ***
> *** PLEASE don't copy me on replies, I'll read them in the group ***

Is there some way to implement new-tags as a function instead of a
macro?  One of my attempts was:
(defun new-tags (&rest tags)
  (dolist (tag tags)
    (new-tag tag)))

but it failed miserably.  In essence, I do not know how to pass a tag
name as the argument that will make it all the way to the new-tag
macro.

Thanks,

Mirko
From: Leandro Rios
Subject: Re: macro that writes macros
Date: 
Message-ID: <477a931e$0$1349$834e42db@reader.greatnowhere.com>
·············@gmail.com escribi�:

> Is there some way to implement new-tags as a function instead of a
> macro?  One of my attempts was:
> (defun new-tags (&rest tags)
>   (dolist (tag tags)
>     (new-tag tag)))
> 
> but it failed miserably.  In essence, I do not know how to pass a tag
> name as the argument that will make it all the way to the new-tag
> macro.
> 

I came up with a function new-tags, but it has two problems:
- The arguments to the function must be quoted, to avoid evaluation

(new-tags 'html 'head 'title 'body 'p)

- it calls eval, and that means that something is going wrong (eval is 
evil!)

(defun new-tags (&rest tags)
   (let ((result nil))
     (eval (cons 'progn
	     (dolist (tag tags result)
	       (push `(new-tag ,tag) result))))))

I have no idea how to make new-tags a function without calling eval. 
Maybe some knowledgeable lisper can give us a hint ?

Leandro
From: ·············@gmail.com
Subject: Re: macro that writes macros
Date: 
Message-ID: <496ed7d0-e626-4299-bc41-9fc4eda04bac@i12g2000prf.googlegroups.com>
On Jan 1, 2:22 pm, Leandro Rios <··················@gmail.com> wrote:
> ·············@gmail.com escribió:
>
> > Is there some way to implement new-tags as a function instead of a
> > macro?  One of my attempts was:
> > (defun new-tags (&rest tags)
> >   (dolist (tag tags)
> >     (new-tag tag)))
>
> > but it failed miserably.  In essence, I do not know how to pass a tag
> > name as the argument that will make it all the way to the new-tag
> > macro.
>
> I came up with a function new-tags, but it has two problems:
> - The arguments to the function must be quoted, to avoid evaluation
>
> (new-tags 'html 'head 'title 'body 'p)
>
> - it calls eval, and that means that something is going wrong (eval is
> evil!)
>
> (defun new-tags (&rest tags)
>    (let ((result nil))
>      (eval (cons 'progn
>              (dolist (tag tags result)
>                (push `(new-tag ,tag) result))))))
>
> I have no idea how to make new-tags a function without calling eval.
> Maybe some knowledgeable lisper can give us a hint ?
>
> Leandro

Thank you Leandro,

I simplified your example as follows:

(defun new-tags (&rest tags)
  (dolist (tag tags)
    (eval `(new-tag ,tag))))

Are there any advantages to your approach of packing it all in a
(progn ... block?

Mirko
From: Leandro Rios
Subject: Re: macro that writes macros
Date: 
Message-ID: <477b92db$0$1341$834e42db@reader.greatnowhere.com>
·············@gmail.com escribi�:

> 
> I simplified your example as follows:
> 
> (defun new-tags (&rest tags)
>   (dolist (tag tags)
>     (eval `(new-tag ,tag))))
> 
> Are there any advantages to your approach of packing it all in a
> (progn ... block?
> 

The difference I see is that I'm calling eval just once. I have no idea 
if it is better performance wise or not. I guess I read so many times 
that eval is bad, that I preferred to call it once and for all. :)

Leandro
From: Kent M Pitman
Subject: Re: macro that writes macros
Date: 
Message-ID: <uve6cb4ym.fsf@nhplace.com>
Leandro Rios <··················@gmail.com> writes:

> ·············@gmail.com escribi�:
> 
> > I simplified your example as follows:
> > (defun new-tags (&rest tags)
> >   (dolist (tag tags)
> >     (eval `(new-tag ,tag))))
> > Are there any advantages to your approach of packing it all in a
> > (progn ... block?
> >
> 
> The difference I see is that I'm calling eval just once. I have no
> idea if it is better performance wise or not. I guess I read so many
> times that eval is bad, that I preferred to call it once and for
> all. :)

Calling EVAL is not something you should do except in very rare
circumstances, which this is not one of.

Calling it fewer times is better only in the sense that calling
anything fewer times is better; the cost of EVAL is that you are
trying to second-guess the normal evaluation process.

In fact, you're better off having a function that constructs the PROGN
and then, rather than evaluating it, just returning that PROGN from a
macro expansion, so that the ordinary processor for the macro can do 
whatever it wants with it (probably compile it into a file).

There's no reason to make this be a function, and there are many
reasons not to.  It SHOULD be a macro.  That's the reason you're
having the trouble.  EVAL is just one of many possible semantic
processors.  By arranging to call it imperatively you are heading off
the possibility of other semantic processing.

See my 1980 paper on macros.  It's about Maclisp, not Common Lisp, so
the syntax is slightly different, but the concepts are still current [and
I have notations on the webbed version to hopefully help with the old-style
syntax somewhat--drop me a line in email if you find confusions]
  http://www.nhplace.com/kent/Papers/Special-Forms.html

In effect, what you're trying to do here, which is why the paper is
relevant, is to re-create all the bad things about FEXPRs.  A FEXPR
was a function that didn't evaluate its arguments.  So it had the
trivial property that you didn't have to write QUOTE around arguments.
But that wasn't the bad thing.  The bad thing was that the arguments
were being quoted, whether you wanted to write the quote or not.  Many
of the uses people put FEXPRs to forced people to call EVAL where they
should have been using macros.  The paper analyzes the places where
macros are better, and this example you have here is just such an
example.
From: ·············@gmail.com
Subject: Re: macro that writes macros
Date: 
Message-ID: <289dd814-958b-4d59-a017-7030da51b8ef@m34g2000hsf.googlegroups.com>
On Jan 2, 10:52 am, Kent M Pitman <······@nhplace.com> wrote:
> Leandro Rios <··················@gmail.com> writes:
> > ·············@gmail.com escribió:
>
> > > I simplified your example as follows:
> > > (defun new-tags (&rest tags)
> > >   (dolist (tag tags)
> > >     (eval `(new-tag ,tag))))
> > > Are there any advantages to your approach of packing it all in a
> > > (progn ... block?
>
> > The difference I see is that I'm calling eval just once. I have no
> > idea if it is better performance wise or not. I guess I read so many
> > times that eval is bad, that I preferred to call it once and for
> > all. :)
>
> Calling EVAL is not something you should do except in very rare
> circumstances, which this is not one of.
... stuff deleted

Thanks Kent,

I did not read your paper (yet).  On a related note, the (eval `(new-
tag ,tag)) construct struck me as looking like a macro -- the
backquote and the comma.  Should the rule be: if it looks like a
macro, it should be a macro.

Mirko
From: Kent M Pitman
Subject: Re: macro that writes macros
Date: 
Message-ID: <ur6gzbquk.fsf@nhplace.com>
·············@gmail.com writes:

> I did not read your paper (yet).  On a related note, the (eval `(new-
> tag ,tag)) construct struck me as looking like a macro -- the
> backquote and the comma.  Should the rule be: if it looks like a
> macro, it should be a macro.

I don't think you should adopt a syntactic reason, no.

The paper goes into some typical reasons why macros are used.  Not
that those are the golden ways of doing things--they're just an
enumeration of some typical reasons that people have been observed to
use macros.

There are no always-right or always-wrong reasons, but the main thing
to understand is that functions are the most common way of abstracting
a description of how to do something based on inputs, and it's probably
reasonable to say to yourself "I should use a function unless I have a
good reason not to".  

A good reason might be as simple as a need to control evaluation time
or evaluation environment or evaluation order.  Or it might be a
desire to use a special syntax.  The reason to use a macro is so that
whatever good reasons you had are all gone away (macro expanded) by
the time the compiler sees things, so that your decision is a private
choice that was resolved at compile time.  If you end up using EVAL,
the choice is not gone away--it affects things like application size,
application speed, and sometimes the 'decidability' of algorithms trying
to analyze your code.

But on the flip side, there are legitimate reasons why programs can
construct data with backquote, even to include programs constructing
other programs, without needing to be macros.  So you can't just go
looking at the shape of the program to decide.  For example, here's a
sample program that I just sketched out to give you the sense that there
could be a program involving backquotes that you would not want to be
a macro...

 (defun my-diff (exp var)
   ;; very oversimplified, not quite correct
   ;; offered only for illustration purposes
   (cond ((eq exp var) 1)
         ((atom exp) 0)
         ((eq (first exp) '+)
          `(+ ,@(mapcar (lambda (x) (my-diff x var)) (rest exp))))
         ((and (eq (first exp) '^) (eq (second exp) var))
          `(* ,(third exp) (^ ,(second exp) (- ,(third exp) 1))))
         (t (error "too complicated"))))

 (my-diff '(+ (^ x 3) x 3) 'x)
 => (+ (* 3 (^ X (- 3 1))) 1 0)

Another example of a program that ght involve backquote but not be a macro
would be the eliza program.

The thing about a macro is that is just a source-to-source translator of
expressions that appear in a source file into expressions that should be
compiled.  When you don't find your program (a) is intended to operate on
programs you've written, the text of which is known at compile time, or 
(b) is intended to output other programs that can be compiled, then that's
a strong clue that something ought not be a macro.  

On the other hand, if it is something that is code to be compiled and
executed you're still calling EVAL, that should raise red flags.  EVAL
is for forcing something to be evaluated that wouldn't be anyway.
Output from a macro will be re-processed looking for definitions
already, and will be executed eventually too.  Why not just results
where they will be executed normally?  It's a bit like cleaning your
house because the maid is coming...

It's all about the problem you're trying to solve.  If you're trying
to write a program for a problem you can't articulate, that's probably
your problem.  It's very hard to learn to program in a void, detached
from some goal.  There's a certain amount of theory you can do that
way, but the theory will be hard to resolve to practice without concrete
situations to connect your ideas to.
From: Pascal Bourguignon
Subject: Re: macro that writes macros
Date: 
Message-ID: <873atf4n9s.fsf@thalassa.informatimago.com>
·············@gmail.com writes:

> On Jan 2, 10:52 am, Kent M Pitman <······@nhplace.com> wrote:
>> Leandro Rios <··················@gmail.com> writes:
>> > ·············@gmail.com escribi�:
>>
>> > > I simplified your example as follows:
>> > > (defun new-tags (&rest tags)
>> > >   (dolist (tag tags)
>> > >     (eval `(new-tag ,tag))))
>> > > Are there any advantages to your approach of packing it all in a
>> > > (progn ... block?
>>
>> > The difference I see is that I'm calling eval just once. I have no
>> > idea if it is better performance wise or not. I guess I read so many
>> > times that eval is bad, that I preferred to call it once and for
>> > all. :)
>>
>> Calling EVAL is not something you should do except in very rare
>> circumstances, which this is not one of.
> ... stuff deleted
>
> Thanks Kent,
>
> I did not read your paper (yet).  On a related note, the (eval `(new-
> tag ,tag)) construct struck me as looking like a macro -- the
> backquote and the comma.  

Backquote and comma are perfectly orthogonal to macros.


> Should the rule be: if it looks like a
> macro, it should be a macro.


Here we use backquote and comma, but it's a plain function:

(defun hello (name)
  (princ `(welcome master ,name))
  (values))

C/USER[103]> (hello 'kenobi)
(WELCOME MASTER KENOBI)



Here we have a good and tru macro, and no backquote or comma:

(defmacro tif (cond then maybe else)
   (list 'case cond
         (list '(t) then)
         (list '(nil) else)
         (list 'otherwise maybe)))

C/USER[106]> (macroexpand-1 '(tif (taller john marc) john '(I do not know) marc))
(CASE (TALLER JOHN MARC) ((T) JOHN) ((NIL) MARC) (OTHERWISE '(I DO NOT KNOW))) ;
T


-- 
__Pascal_Bourguignon__               _  Software patents are endangering
()  ASCII ribbon against html email (o_ the computer industry all around
/\  1962:DO20I=1.100                //\ the world http://lpf.ai.mit.edu/
    2001:my($f)=`fortune`;          V_/   http://petition.eurolinux.org/
From: Leandro Rios
Subject: Re: macro that writes macros
Date: 
Message-ID: <477d0f59$0$1341$834e42db@reader.greatnowhere.com>
Kent M Pitman escribi�:
> 
> Calling EVAL is not something you should do except in very rare
> circumstances, which this is not one of.
> 
> Calling it fewer times is better only in the sense that calling
> anything fewer times is better; the cost of EVAL is that you are
> trying to second-guess the normal evaluation process.
> 
> In fact, you're better off having a function that constructs the PROGN
> and then, rather than evaluating it, just returning that PROGN from a
> macro expansion, so that the ordinary processor for the macro can do 
> whatever it wants with it (probably compile it into a file).
> 
> There's no reason to make this be a function, and there are many
> reasons not to.  It SHOULD be a macro.  That's the reason you're
> having the trouble.  EVAL is just one of many possible semantic
> processors.  By arranging to call it imperatively you are heading off
> the possibility of other semantic processing.
> 
> See my 1980 paper on macros.  It's about Maclisp, not Common Lisp, so
> the syntax is slightly different, but the concepts are still current [and
> I have notations on the webbed version to hopefully help with the old-style
> syntax somewhat--drop me a line in email if you find confusions]
>   http://www.nhplace.com/kent/Papers/Special-Forms.html
> 
> In effect, what you're trying to do here, which is why the paper is
> relevant, is to re-create all the bad things about FEXPRs.  A FEXPR
> was a function that didn't evaluate its arguments.  So it had the
> trivial property that you didn't have to write QUOTE around arguments.
> But that wasn't the bad thing.  The bad thing was that the arguments
> were being quoted, whether you wanted to write the quote or not.  Many
> of the uses people put FEXPRs to forced people to call EVAL where they
> should have been using macros.  The paper analyzes the places where
> macros are better, and this example you have here is just such an
> example.
> 

Kent, thank you for taking the time to make the concept clear to me. I 
read your paper and had no trouble at all with Maclisp's syntax. Now I 
see why eval should not be used without a good reason. But, being the 
cases where it is appropiate to use eval so few, would you mind to put 
an example where it could be advisable to use it? I can only think of 
code generated at runtime (i.e., input from the user), which cannot be 
foreseen at compile time.

Thanks again, and I hope not to be abusing your good will.

Leandro
From: Kent M Pitman
Subject: Re: macro that writes macros
Date: 
Message-ID: <u7iiqdyjf.fsf@nhplace.com>
Leandro Rios <··················@gmail.com> writes:

> Kent, thank you for taking the time to make the concept clear to me. I
> read your paper and had no trouble at all with Maclisp's syntax. Now I
> see why eval should not be used without a good reason. But, being the
> cases where it is appropiate to use eval so few, would you mind to put
> an example where it could be advisable to use it? I can only think of
> code generated at runtime (i.e., input from the user), which cannot be
> foreseen at compile time.

Well, yes, certain situations involving automatic programming might
need it, though often various tricks can be done to dodge the need for
eval in favor of funcalling precompiled things.  Some scripting
situations, patch loading, interactive debugging, testing, embedded
languages, etc.  But the cases are really rare and most often the
common wisdom is that you should first assume there are other ways to
do what you want to do and fall to EVAL only either as a last resort
or a quick-and-dirty thing.  People would almost never advise you that
you should use EVAL, they would mostly just forgive you various uses. :)

It's often useful in interactive testing, too, to write a one-shot
program interactively that constructs and executes some test.  But
most rules of style don't really apply for interactive use--often,
interactively, whatever is easiest to type is ok.

One reason EVAL is avoided that may or may not have been mentioned in
the paper is that delivered Lisp applications often want to "gc" away
(either literally or figuratively) parts of the system that are not in
use.  So in addition to speed, there's the issue that the evaluator
and compiler are usually big enough parts of the system that one would
prefer not to have to include them or link them in a delivered app...
or, at least, not for a trivial reason.

> Thanks again, and I hope not to be abusing your good will.

Not at all.  I respond when I can and don't otherwise.
From: Vassil Nikolov
Subject: Re: macro that writes macros
Date: 
Message-ID: <snwlk765fel.fsf@luna.vassil.nikolov.names>
Kent M Pitman <······@nhplace.com> writes:
> [on using EVAL]
> Well, yes, certain situations involving automatic programming might
> need it, though often various tricks can be done to dodge the need for
> eval in favor of funcalling precompiled things.  Some scripting
> situations, patch loading, interactive debugging, testing, embedded
> languages, etc.  But the cases are really rare and most often the
> common wisdom is that you should first assume there are other ways to
> do what you want to do and fall to EVAL only either as a last resort
> or a quick-and-dirty thing.  People would almost never advise you that
> you should use EVAL, they would mostly just forgive you various uses. :)

  By the way, is there a good, solid "canonical" document that
  elaborates on the reasons not to use EVAL and the cases when its use
  is warranted?  I have searched for something like that in the past,
  but never found anything.  Every so often, someone suggests a
  gratuitous use of EVAL (and that goes beyond Lisp, as quite a few
  languages now have it, Java included), and that makes me wish there
  was a pointer that could simply be used in response.

  I understand that it would take an expert to write one, and experts'
  time is expensive; I am asking in the hope that it already exists,
  but is hard to find.

  ---Vassil.


-- 
Bound variables, free programmers.
From: Kent M Pitman
Subject: Re: macro that writes macros
Date: 
Message-ID: <u4pdta9si.fsf@nhplace.com>
Vassil Nikolov <···············@pobox.com> writes:

>   By the way, is there a good, solid "canonical" document that
>   elaborates on the reasons not to use EVAL and the cases when its use
>   is warranted?

I think it's a choice point or design trade-off, not a recommendation.
The notion of a canonical answer to a design trade-off or to a choice
point is hard for me to grasp.

I think the reasons for it being ok vary situation to situation.  One
person's absolute reason not to use it is not another's, and I would
warn against approaching the problem that way lest you close doors that
you should not.

It's more about questions that need answering than about answers to those
questions.
From: Vassil Nikolov
Subject: Re: macro that writes macros
Date: 
Message-ID: <snwd4sh5zn9.fsf@luna.vassil.nikolov.names>
Kent M Pitman <······@nhplace.com> writes:

> Vassil Nikolov <···············@pobox.com> writes:
>
>>   By the way, is there a good, solid "canonical" document that
>>   elaborates on the reasons not to use EVAL and the cases when its use
>>   is warranted?
>
> I think it's a choice point or design trade-off, not a recommendation.
> The notion of a canonical answer to a design trade-off or to a choice
> point is hard for me to grasp.
>
> I think the reasons for it being ok vary situation to situation.  One
> person's absolute reason not to use it is not another's, and I would
> warn against approaching the problem that way lest you close doors that
> you should not.
>
> It's more about questions that need answering than about answers to those
> questions.

  I shouldn't have used "canonical" there, not even in quotes.  I was
  thinking of something giving a more detailed explanation---more
  detailed than what occurs in e.g. usenet posts---of at least some
  concrete reasons why one would not want to use EVAL in this case or
  that, or why one would in a third or fourth case.  Perhaps something
  like a mini case study (of several representative cases).  I am
  certainly not thinking about a prescription (or a proscription).

  I have seen snippets here and there---just as one example, the point
  being made that SYMBOL-VALUE is the thing to use in many cases where
  people have used EVAL,---but not a single comprehensive exposition.
  One may well exist, though, which is why I asked.

  This is perhaps one of those Infrequently Answered Questions...

  ---Vassil.


-- 
Bound variables, free programmers.
From: Kent M Pitman
Subject: Re: macro that writes macros
Date: 
Message-ID: <utzltunkm.fsf@nhplace.com>
Vassil Nikolov <···············@pobox.com> writes:

>   I have seen snippets here and there---just as one example, the point
>   being made that SYMBOL-VALUE is the thing to use in many cases where
>   people have used EVAL,---but not a single comprehensive exposition.

Although in many such cases, if it's lexically apparent, the compiler
should be able to optimize that call to EVAL to a call to SYMBOL-VALUE
so that the damage in some cases is avoided.  It's not reliable, though.

>   One may well exist, though, which is why I asked.
> 
>   This is perhaps one of those Infrequently Answered Questions...

Well, maybe.  I think it's fair to say that it's more infrequently asked
than it should be, though.  It's part of the hurdle of learning Lisp--
getting past the mythology that Lisp is all about this.
From: Pascal Bourguignon
Subject: Re: macro that writes macros
Date: 
Message-ID: <877iip2xua.fsf@thalassa.informatimago.com>
Vassil Nikolov <···············@pobox.com> writes:

> Kent M Pitman <······@nhplace.com> writes:
>> [on using EVAL]
>> Well, yes, certain situations involving automatic programming might
>> need it, though often various tricks can be done to dodge the need for
>> eval in favor of funcalling precompiled things.  Some scripting
>> situations, patch loading, interactive debugging, testing, embedded
>> languages, etc.  But the cases are really rare and most often the
>> common wisdom is that you should first assume there are other ways to
>> do what you want to do and fall to EVAL only either as a last resort
>> or a quick-and-dirty thing.  People would almost never advise you that
>> you should use EVAL, they would mostly just forgive you various uses. :)
>
>   By the way, is there a good, solid "canonical" document that
>   elaborates on the reasons not to use EVAL and the cases when its use
>   is warranted?  I have searched for something like that in the past,
>   but never found anything.  Every so often, someone suggests a
>   gratuitous use of EVAL (and that goes beyond Lisp, as quite a few
>   languages now have it, Java included), and that makes me wish there
>   was a pointer that could simply be used in response.
>
>   I understand that it would take an expert to write one, and experts'
>   time is expensive; I am asking in the hope that it already exists,
>   but is hard to find.

Perhaps googling for EVAL in c.l.l. would give a comprehensive list,
for anybody valient enough to summarize all the articles.

-- 
__Pascal_Bourguignon__               _  Software patents are endangering
()  ASCII ribbon against html email (o_ the computer industry all around
/\  1962:DO20I=1.100                //\ the world http://lpf.ai.mit.edu/
    2001:my($f)=`fortune`;          V_/   http://petition.eurolinux.org/
From: Vassil Nikolov
Subject: Re: macro that writes macros
Date: 
Message-ID: <snw4pdt5orw.fsf@luna.vassil.nikolov.names>
Pascal Bourguignon <···@informatimago.com> writes:
> Perhaps googling for EVAL in c.l.l. would give a comprehensive list,
> [of cases for or against EVAL]
> for anybody valient enough to summarize all the articles.

  Alas, that would require very copious free time (Google counts 9,210
  hits for ``"eval" group:comp.lang.lisp''; it might be more feasible
  to write a robot that examines them (if Google Groups allow that)).

  ---Vassil.


-- 
Bound variables, free programmers.
From: Pascal Bourguignon
Subject: Re: macro that writes macros
Date: 
Message-ID: <87sl1h471s.fsf@thalassa.informatimago.com>
·············@gmail.com writes:

> On Dec 29 2007, 9:44 pm, Barry Margolin <······@alum.mit.edu> wrote:
>> In article
>> <····································@e23g2000prf.googlegroups.com>,
>> [...]
>> What you want is:
>>
>> (defmacro new-tags (&rest tags)
>>   `(progn
>>      ,(loop for tag in tags
>>             collect `(new-tag ,tag))))
>>
> Is there some way to implement new-tags as a function instead of a
> macro?  

Even more, a new-tags implemented as a function is sometimes needed.

Assume you're reading a DTD, and trying to build at run-time the
macros, from the elements you found at run-time in the DTD.  Then
using a new-tags macro would be very awkward: a new-tags function is
needed.

By the way, for a macro, a name like deftags or define-tags would be
more conventional.  


> One of my attempts was:
> (defun new-tags (&rest tags)
>   (dolist (tag tags)
>     (new-tag tag)))
>
> but it failed miserably.  In essence, I do not know how to pass a tag
> name as the argument that will make it all the way to the new-tag
> macro.

Forget macros for a moment.


For a function, it often easier to use it when the lists are passed as
a single list parameter.

(defun make-tags (tags)
  (dolist (tag tags) 
     (make-tag tag)))


(defun make-tag-function (tag)
    (let ((open (format nil "<~(~A~)>~%"  tag))
          (clos (format nil "</~(~A~)>~%" tag)))
       (lambda (contents-thunk)
           (princ open) (funcall contents-thunk) (princ clos) (values))))

(defun make-tag (tag)
   (setf (symbol-function tag) (make-tag-function tag)))


So now you can write:


(make-tags '(chapter section para bold))


(chapter
 (lambda () 
   (section
    (lambda ()
      (para
       (lambda ()
         (princ "L'algorithme en  question  a  ete  publie  en  1960")(terpri)
         (princ "dans l'IBM Journal, article intitule �Toward  Mechanical Mathematics�,")(terpri)
         (princ "avec des variantes et une  extension au calcul  des  predicats.  Il")(terpri)
         (princ "s'agit  ici  du  �premier programme� de Wang, systeme �P�.")(terpri)))
      (para
       (lambda ()
         (princ "L'article a ete ecrit en 1958, et les experiences effectuees sur IBM 704")(terpri)
         (princ "- machine a lampes, 32 k  mots  de 36 bits, celle-la meme qui vit naitre")(terpri)
         (princ "LISP a la meme epoque. Le programme  a  ete ecrit en assembleur (Fortran")(terpri)
         (princ "existait, mais il ne s'etait pas encore impose)  et  l'auteur estime que")(terpri)
         (princ "�there is very little in the program that is not straightforward�.")(terpri)))))))


Note that once you have the functions, you can trivially get the macros:

(defmacro define-tag (tag)
  (let ((fun (gensym)))
    `(let ((,fun (make-tag-function ',tag)))
        (defmacro ,tag (&body body)
           `(funcall ,,fun (lambda () ,@body))))))

(defmacro define-tags (&rest tags)
  `(progn ,@(loop :for tag :in tags :collect `(define-tag ,tag))))

     
So you can write:

(define-tags chapter section para)


(chapter
   (section
      (para
         (princ "L'algorithme en  question  a  ete  publie  en  1960")(terpri)
         (princ "dans l'IBM Journal, article intitule �Toward  Mechanical Mathematics�,")(terpri)
         (princ "avec des variantes et une  extension au calcul  des  predicats.  Il")(terpri)
         (princ "s'agit  ici  du  �premier programme� de Wang, systeme �P�.")(terpri))
      (para
         (princ "L'article a ete ecrit en 1958, et les experiences effectuees sur IBM 704")(terpri)
         (princ "- machine a lampes, 32 k  mots  de 36 bits, celle-la meme qui vit naitre")(terpri)
         (princ "LISP a la meme epoque. Le programme  a  ete ecrit en assembleur (Fortran")(terpri)
         (princ "existait, mais il ne s'etait pas encore impose)  et  l'auteur estime que")(terpri)
         (princ "�there is very little in the program that is not straightforward�.")(terpri))))


Also, you may need both the macro and the functions in the same
program, in which case we could name a function TAG* corresponding to
the macro TAG.  Then define-tag can be simplier:

(defmacro define-tag (tag)
  (let ((funame (intern (format nil "~A*" tag))))
   `(progn
       (setf (symbol-function ',funame) (make-tag-function ',tag))
       (defmacro ,tag (&body body)
          `(,',funame (lambda () ,@body))))))


-- 
__Pascal_Bourguignon__               _  Software patents are endangering
()  ASCII ribbon against html email (o_ the computer industry all around
/\  1962:DO20I=1.100                //\ the world http://lpf.ai.mit.edu/
    2001:my($f)=`fortune`;          V_/   http://petition.eurolinux.org/
From: ·············@gmail.com
Subject: Re: macro that writes macros
Date: 
Message-ID: <341b32fc-3a92-4ae8-a8bb-029628e4d660@d4g2000prg.googlegroups.com>
On Jan 1, 3:36 pm, Pascal Bourguignon <····@informatimago.com> wrote:
> ·············@gmail.com writes:
> > On Dec 29 2007, 9:44 pm, Barry Margolin <······@alum.mit.edu> wrote:
> >> In article
> >> <····································@e23g2000prf.googlegroups.com>,
> >> [...]
> >> What you want is:
>
> >> (defmacro new-tags (&rest tags)
> >>   `(progn
> >>      ,(loop for tag in tags
> >>             collect `(new-tag ,tag))))
>
> > Is there some way to implement new-tags as a function instead of a
> > macro?
>
>

... stuff deleted ...

> Forget macros for a moment.
>
> For a function, it often easier to use it when the lists are passed as
> a single list parameter.
>
> (defun make-tags (tags)
>   (dolist (tag tags)
>      (make-tag tag)))
>
> (defun make-tag-function (tag)
>     (let ((open (format nil "<~(~A~)>~%"  tag))
>           (clos (format nil "</~(~A~)>~%" tag)))
>        (lambda (contents-thunk)
>            (princ open) (funcall contents-thunk) (princ clos) (values))))
>
> (defun make-tag (tag)
>    (setf (symbol-function tag) (make-tag-function tag)))

I think I understand what you are trying to do (and I finally get how
one can write self-generating code in Lisp and how the similarity of
data and code contribute to that.  The (setf ... construct strikes me
as brain surgery being performed by lisp on itself))

>
> So now you can write:
>
> (make-tags '(chapter section para bold))

I get an error if I try to invoke these environments.  For example,
(para "some text")
gives an error message that "some text" is not a function.  I
understand why, since it is being passed to the (funcall ... in make-
tag-function.   Am I supposed to pass it something line (print
"text")?  I tried, unsuccessfully,
(para #'print "text"))

... rest deleted ...

Thanks,

Mirko
From: Pascal Bourguignon
Subject: Re: macro that writes macros
Date: 
Message-ID: <87ejd03sbt.fsf@thalassa.informatimago.com>
·············@gmail.com writes:

> On Jan 1, 3:36 pm, Pascal Bourguignon <····@informatimago.com> wrote:
>> ·············@gmail.com writes:
>> > On Dec 29 2007, 9:44 pm, Barry Margolin <······@alum.mit.edu> wrote:
>> >> In article
>> >> <····································@e23g2000prf.googlegroups.com>,
>> >> [...]
>> >> What you want is:
>>
>> >> (defmacro new-tags (&rest tags)
>> >>   `(progn
>> >>      ,(loop for tag in tags
>> >>             collect `(new-tag ,tag))))
>>
>> > Is there some way to implement new-tags as a function instead of a
>> > macro?
>>
>>
>
> ... stuff deleted ...
>
>> Forget macros for a moment.
>>
>> For a function, it often easier to use it when the lists are passed as
>> a single list parameter.
>>
>> (defun make-tags (tags)
>>   (dolist (tag tags)
>>      (make-tag tag)))
>>
>> (defun make-tag-function (tag)
>>     (let ((open (format nil "<~(~A~)>~%"  tag))
>>           (clos (format nil "</~(~A~)>~%" tag)))
>>        (lambda (contents-thunk)
>>            (princ open) (funcall contents-thunk) (princ clos) (values))))
>>
>> (defun make-tag (tag)
>>    (setf (symbol-function tag) (make-tag-function tag)))
>
> I think I understand what you are trying to do (and I finally get how
> one can write self-generating code in Lisp and how the similarity of
> data and code contribute to that.  The (setf ... construct strikes me
> as brain surgery being performed by lisp on itself))


(setf symbol-function) is the essential behavior or DEFUN.  DEFUN may
expand to some more stuff, including implementation dependant
behavior, but its main effect is to bind the symbol-function slot of
the name of the function.  You could implement a 'naive' defun as:

(defpackage "MYCL"
  (:use "CL") 
  (:shadow "DEFUN"))

(defmacro MYCL:DEFUN (name lambda-list &body body)
   (multiple-value-bind (docstring declarations body) (parse-body body)
    `(setf (symbol-function ',name)
              (lambda ,lambda-list
                ,@(when docstring (list docstring))
                ,@declarations
                (block ,name ,@body))
           (documentation ',name 'function)
              (or docstring "Undocumented function"))))

;; You could implement PARSE-BODY as:

(defun parse-body (body)
  ;; see http://darcs.informatimago.com/darcs/public/lisp/common-lisp/source-form.lisp
  (values
    (com.informatimago.common-lisp.source-form:extract-documentation body)
    (com.informatimago.common-lisp.source-form:extract-declarations body)
    (com.informatimago.common-lisp.source-form:extract-body body)))



>> So now you can write:
>>
>> (make-tags '(chapter section para bold))
>
> I get an error if I try to invoke these environments.  For example,
> (para "some text")
> gives an error message that "some text" is not a function.  I
> understand why, since it is being passed to the (funcall ... in make-
> tag-function.   Am I supposed to pass it something line (print
> "text")?  I tried, unsuccessfully,
> (para #'print "text"))

Read the error message, read the examples, or read the code of
MAKE-TAG-FUNCTION!

You have to do some type inference, since unfortunately for you, I
haven't documented these functions.  But would you have read the
docstrings if I had provided them, I wonder...


The error message tells you it wants a function, so trying (function
print) is one step in the right direction, but you should persist.  As
you've seen,   (para #'print "text") gives:

*** - EVAL/APPLY: too many arguments given to :LAMBDA 
Which means that PARA takes only one argument. So let's try:
(para #'print) and we get:

<para>

*** - APPLY: too few arguments given to PRINT

which tells us that PARA won't give enough arguments to PRINT: it
expects a function that takes NO argument.
(para (lambda () #|what could we do here?|#)) successfully produces:

<para>
</para>

and (para (lambda () (princ "Some text"))) produces:

<para>
Some text
</para>



Of course, you could've seen that just reading the examples I gave.




Another way to find the same usage information would be reading the source:

 (defun make-tag-function (tag)
     (let ((open (format nil "<~(~A~)>~%"  tag))
           (clos (format nil "</~(~A~)>~%" tag)))
        (lambda (contents-thunk)
            (princ open) (funcall contents-thunk) (princ clos) (values))))

make-tag-function returns an anonymous function 'lambda', which takes
ONE argument named CONTENTS-THUNK.  THUNK is a conventional name for
this kind of functions that take no argument, and that will be called
in place of a 'body'.  But even if you don't know that, you can go on
reading the source and see that CONTENTS-THUNK is used once, as
argument of FUNCALL: (funcall contents-thunk) and therefore that
contents-thunk MUST be a function that can take ZERO argument (it
could accept more, but it won't get more from here). You could also
notice that this function is called in the middle of printing the OPEN
and CLOS strings, and thus guess that it might be a good idea in any
function passed as CONTENTS-THUNK to print what you would like to have
between the OPEN and CLOS.  And then you could write directly:

  (para  (lambda ()  (princ "Hi there")))

and get:

<para>
Hi there
</para>

 
  

-- 
__Pascal_Bourguignon__               _  Software patents are endangering
()  ASCII ribbon against html email (o_ the computer industry all around
/\  1962:DO20I=1.100                //\ the world http://lpf.ai.mit.edu/
    2001:my($f)=`fortune`;          V_/   http://petition.eurolinux.org/
From: ·············@gmail.com
Subject: Re: macro that writes macros
Date: 
Message-ID: <09f80a26-8484-4274-94fb-a8688a3da682@i7g2000prf.googlegroups.com>
On Jan 2, 3:06 pm, Pascal Bourguignon <····@informatimago.com> wrote:
> ·············@gmail.com writes:
> > On Jan 1, 3:36 pm, Pascal Bourguignon <····@informatimago.com> wrote:
> >> ·············@gmail.com writes:
> >> > On Dec 29 2007, 9:44 pm, Barry Margolin <······@alum.mit.edu> wrote:
> >> >> In article
> >> >> <····································@e23g2000prf.googlegroups.com>,
> >> >> [...]
> >> >> What you want is:
>
> >> >> (defmacro new-tags (&rest tags)
> >> >>   `(progn
> >> >>      ,(loop for tag in tags
> >> >>             collect `(new-tag ,tag))))
>
> >> > Is there some way to implement new-tags as a function instead of a
> >> > macro?
>
> > ... stuff deleted ...
>
> >> Forget macros for a moment.
... much stuff deleted

> > I get an error if I try to invoke these environments.  For example,
> > (para "some text")
> > gives an error message that "some text" is not a function.  I
> > understand why, since it is being passed to the (funcall ... in make-
> > tag-function.   Am I supposed to pass it something line (print
> > "text")?  I tried, unsuccessfully,
> > (para #'print "text"))
>
> Read the error message, read the examples, or read the code of
> MAKE-TAG-FUNCTION!
>
> You have to do some type inference, since unfortunately for you, I
> haven't documented these functions.  But would you have read the
> docstrings if I had provided them, I wonder...


First, thank you for the explanation.  Second, it did hurt, ..., but
not all hurt is totally undeserved :-)

Actually, I did study your example, and learned quite a bit about the
usage of (setf and (lambda.  It took me a bit to understand what you
were trying to do, but I think that I did get it.  I got hung up on
(funcall contents-thunk).  My erroneous understanding of funcall was
that it is used to apply a function *to a bunch of arguments*.  I
mixed it up with map.

>
> The error message tells you it wants a function, so trying (function
> print) is one step in the right direction, but you should persist.  As
> you've seen,   (para #'print "text") gives:
>
> *** - EVAL/APPLY: too many arguments given to :LAMBDA
> Which means that PARA takes only one argument. So let's try:
> (para #'print) and we get:
>
> <para>
>
> *** - APPLY: too few arguments given to PRINT
>
> which tells us that PARA won't give enough arguments to PRINT: it
> expects a function that takes NO argument.
> (para (lambda () #|what could we do here?|#)) successfully produces:
>
> <para>
> </para>
>
> and (para (lambda () (princ "Some text"))) produces:
>
> <para>
> Some text
> </para>
>
> Of course, you could've seen that just reading the examples I gave.

I was trying to reply to you before I was done playing with it.  I did
not want to leave you with the impression that your post, into which
you did put some effort, went unread.  My intention was not clear.

Anyway, thanks again.  I'm going "crawling back to my hole" to study
all the material that you have laid out.

Mirko
From: Leandro Rios
Subject: Re: macro that writes macros
Date: 
Message-ID: <477714a8$0$1342$834e42db@reader.greatnowhere.com>
·····················@gmail.com escribi�:
> Hi,
> 
> I'm learning lisp and tried to make this little program to write a web
> page:
> 
> 
> (defmacro new-tag (name)
>   (let ((sname (string-downcase (symbol-name name))))
>     `(defmacro ,name (&rest content)
>        `(progn
> 	  (format t "<~a>~%" ,',sname)
> 	  ,@content
> 	  (format t "</~a>~%" ,',sname)))))
> 
> (new-tag html)
> (new-tag head)
> (new-tag title)
> (new-tag body)
> (new-tag p)
> 
> ;; With that, now a web page can be "self-generated":
> (html
>  (head
>   (title "My web page"))
>  (body
>   (p "Hello world")))
> 
> 
> And it works. But then I thought about combining all those "(new-
> tag ...)" like this:
> 

Em, no, it does not. When I run your code with your example I get:

<html>
<head>
<title>
</title>
</head>
<body>
<p>
</p>
</body>
</html>

You're omitting to print the strings. The macroexpansion of (new-tag 
title) is (try macroexpand and macroexpand-1):

(DEFMACRO TITLE (&REST CONTENT)
   `(PROGN (FORMAT T "<~a>~%" "title") ,@CONTENT (FORMAT T "</~a>~%" 
"title")))

And that of (title "My web page") results in:

(PROGN (FORMAT T "<~a>~%" "title") "My web page" (FORMAT T "</~a>~%" 
"title"))

As you can see, the string "My web page" isn't going to the same stream 
as the tags (in this case standard output). In fact, it's going nowhere.

I modified your version so it prints the string when present:

(defmacro new-tag (name)
   (let ((sname (string-downcase (symbol-name name))))
     `(defmacro ,name (&rest content)
        `(progn
	  (format t "<~a>~%" ,',sname)
	  (if (typep (car ',content) 'string)
	      (format t "~A~%" (car ',content))
	      (progn ,@content))
	  (format t "</~a>~%" ,',sname)))))

Now the output seems correct:

<html>
<head>
<title>
My web page
</title>
</head>
<body>
<p>
Hello world
</p>
</body>
</html>


Others have already showed how to fix new-tags.

Happy lisping and happy new year.

Leandro
From: jordi
Subject: Re: macro that writes macros
Date: 
Message-ID: <588075dc-2b2d-4f79-8651-c30ed72d046b@e25g2000prg.googlegroups.com>
On Dec 30, 4:46 am, Leandro Rios <··················@gmail.com> wrote:
> ·····················@gmail.com escribió:
>
>
>
> > Hi,
>
> > I'm learning lisp and tried to make this little program to write a web
> > page:
>
> > (defmacro new-tag (name)
> >   (let ((sname (string-downcase (symbol-name name))))
> >     `(defmacro ,name (&rest content)
> >        `(progn
> >      (format t "<~a>~%" ,',sname)
> >      ,@content
> >      (format t "</~a>~%" ,',sname)))))
>
> > (new-tag html)
> > (new-tag head)
> > (new-tag title)
> > (new-tag body)
> > (new-tag p)
>
> > ;; With that, now a web page can be "self-generated":
> > (html
> >  (head
> >   (title "My web page"))
> >  (body
> >   (p "Hello world")))
>
> > And it works. But then I thought about combining all those "(new-
> > tag ...)" like this:
>
> Em, no, it does not. When I run your code with your example I get:
>
> <html>
> <head>
> <title>
> </title>
> </head>
> <body>
> <p>
> </p>
> </body>
> </html>
>
> You're omitting to print the strings. The macroexpansion of (new-tag
> title) is (try macroexpand and macroexpand-1):
>
> (DEFMACRO TITLE (&REST CONTENT)
>    `(PROGN (FORMAT T "<~a>~%" "title") ,@CONTENT (FORMAT T "</~a>~%"
> "title")))
>
> And that of (title "My web page") results in:
>
> (PROGN (FORMAT T "<~a>~%" "title") "My web page" (FORMAT T "</~a>~%"
> "title"))
>
> As you can see, the string "My web page" isn't going to the same stream
> as the tags (in this case standard output). In fact, it's going nowhere.
>
> I modified your version so it prints the string when present:
>
> (defmacro new-tag (name)
>    (let ((sname (string-downcase (symbol-name name))))
>      `(defmacro ,name (&rest content)
>         `(progn
>           (format t "<~a>~%" ,',sname)
>           (if (typep (car ',content) 'string)
>               (format t "~A~%" (car ',content))
>               (progn ,@content))
>           (format t "</~a>~%" ,',sname)))))
>
> Now the output seems correct:
>
> <html>
> <head>
> <title>
> My web page
> </title>
> </head>
> <body>
> <p>
> Hello world
> </p>
> </body>
> </html>
>
> Others have already showed how to fix new-tags.
>
> Happy lisping and happy new year.
>
> Leandro

Yes, you are right, I was so concerned with the second macro that I
didn't notice it. Your solution works nicely. Thank you!

jordi
From: John Thingstad
Subject: Re: macro that writes macros
Date: 
Message-ID: <op.t34m9ff3ut4oq5@pandora.alfanett.no>
P� Sun, 30 Dec 2007 03:13:39 +0100, skrev  
<·····················@gmail.com>:

> And it works. But then I thought about combining all those "(new-
> tag ...)" like this:
>
> (defmacro new-tags (&rest tags)
>   (dolist (tag tags)
>     `(new-tag ,tag)))
>
> (new-tags html head title body p)
>
> But it doesn't work! I tried without the "`" nor the "," in the "new-
> tags" definition and it doesn't work either. I have been fighting with
> this simple-looking problem for a long time, reading docs and stuff,
> and I can't make it work anyway nor see what is wrong! Anyone has some
> advice on what's going on or what I should check?
>

Well if you try to macroexpand this you will notice that the macro return  
nil.
This is because although dolist iterates over the list it don't return the  
elements.

(defmacro new-tags (&rest tags)
   (let ((defs (list 'progn)))
     (dolist (tag tags)
       (push `(new-tag ,tag) defs))
     (nreverse defs)))

CL-USER 3 > (pprint (macroexpand '(new-tags html head title body p)))

(PROGN
   (NEW-TAG HTML)
   (NEW-TAG HEAD)
   (NEW-TAG TITLE)
   (NEW-TAG BODY)
   (NEW-TAG P))

which is probably what you want.	

--------------
John Thingstad
From: Sacha
Subject: Re: macro that writes macros
Date: 
Message-ID: <fRHdj.2917$jG4.922995@phobos.telenet-ops.be>
·····················@gmail.com wrote:
> (defmacro new-tag (name)
>   (let ((sname (string-downcase (symbol-name name))))
>     `(defmacro ,name (&rest content)
>        `(progn
> 	  (format t "<~a>~%" ,',sname)
> 	  ,@content
> 	  (format t "</~a>~%" ,',sname)))))
> 

This is the occasion for me to ask how to get rid of these ','symbol 
things. While in this case it's pretty obvious how they work, it 
sometimes make the code pretty hard to read and understand. I recently 
came up with a very subtle bug (for me anyways) using such syntax.

So what are you guys usually doing to avoid these ? Do you avoid these 
at all ?

Thanks in advance,
Sacha
From: Dan Bensen
Subject: Re: macro that writes macros
Date: 
Message-ID: <fl7n86$v4g$1@wildfire.prairienet.org>
Sacha wrote:
> how to get rid of these ','symbol things.

Try here:
http://bc.tech.coop/blog/041205.html

> So what are you guys usually doing to avoid these ? 
> Do you avoid these at all ?

I've been using them.  The secret seems to be that the innermost
backquote is associated with the leftmost comma, but the comma
isn't evaluated.  So backquote-comma pairing does occur for the
inner backquote, even though it's quoted by the outer backquote,
but evaluation of the comma does not occur.  I guess the comma
is quoted because its backquote is quoted.  But somehow that
doesn't prevent READ and/or EVAL from pairing them up.

(defvar bar :bar)
``(,foo ,',bar) => `(,FOO :BAR)

bar is eval'd because the second comma before it belongs to the
outer backquote, which is eval'd.  foo is not eval'd because
its comma belongs to the uneval'd inner backquote.  What I don't
understand is why the ,' before ,bar disappears.  Even though
it's a no-op, it seems like something is automagically evaluating
or pruning it when it should be quoted.

-- 
Dan
www.prairienet.org/~dsb/
From: Sacha
Subject: Re: macro that writes macros
Date: 
Message-ID: <l5rgj.16674$iN7.2358507@phobos.telenet-ops.be>
Dan Bensen wrote:
> Sacha wrote:
>> how to get rid of these ','symbol things.
> 
> Try here:
> http://bc.tech.coop/blog/041205.html
> 
>> So what are you guys usually doing to avoid these ? Do you avoid these 
>> at all ?
> 
> I've been using them.  The secret seems to be that the innermost
> backquote is associated with the leftmost comma, but the comma
> isn't evaluated.  So backquote-comma pairing does occur for the
> inner backquote, even though it's quoted by the outer backquote,
> but evaluation of the comma does not occur.  I guess the comma
> is quoted because its backquote is quoted.  But somehow that
> doesn't prevent READ and/or EVAL from pairing them up.
> 
> (defvar bar :bar)
> ``(,foo ,',bar) => `(,FOO :BAR)
> 
> bar is eval'd because the second comma before it belongs to the
> outer backquote, which is eval'd.  foo is not eval'd because
> its comma belongs to the uneval'd inner backquote.  What I don't
> understand is why the ,' before ,bar disappears.  Even though
> it's a no-op, it seems like something is automagically evaluating
> or pruning it when it should be quoted.
> 

Sorry i had to leave home for a few days. Thanks for this info, the blog 
post is nice !

Sacha
From: Pascal Bourguignon
Subject: Re: macro that writes macros
Date: 
Message-ID: <87ejd4548w.fsf@thalassa.informatimago.com>
Sacha <····@address.spam> writes:

> ·····················@gmail.com wrote:
>> (defmacro new-tag (name)
>>   (let ((sname (string-downcase (symbol-name name))))
>>     `(defmacro ,name (&rest content)
>>        `(progn
>> 	  (format t "<~a>~%" ,',sname)
>> 	  ,@content
>> 	  (format t "</~a>~%" ,',sname)))))
>> 
>
> This is the occasion for me to ask how to get rid of these ','symbol
> things. While in this case it's pretty obvious how they work, it
> sometimes make the code pretty hard to read and understand. I recently
> came up with a very subtle bug (for me anyways) using such syntax.
>
> So what are you guys usually doing to avoid these ? Do you avoid these
> at all ?

Don't use backquote.


(defmacro new-tag (name)
  (let ((sname (string-downcase (symbol-name name))))
    (list 'defmacro name '(&rest content)
          (list 'append
                (list 'list (list 'quote 'progn)
                      (list 'list (list 'quote 'format) (list 'quote 't) (list 'quote '"<~a>~%")
                            (list 'list (list 'quote 'quote) sname)))
                'content
                (list 'list
                      (list 'list (list 'quote 'format) (list 'quote 't) (list 'quote '"</~a>~%")
                            (list 'list (list 'quote 'quote) sname)))))))


C/USER[67]> (macroexpand-1 '(new-tag div))
(DEFMACRO DIV (&REST CONTENT)
 (APPEND (LIST 'PROGN (LIST 'FORMAT 'T '"<~a>~%" (LIST 'QUOTE #1="div"))) CONTENT
  (LIST (LIST 'FORMAT 'T '"</~a>~%" (LIST 'QUOTE #1#))))) ;
T
        

Note that to build these list expressions, this function may be handy:

(defun kind-of-list (list)
  (labels ((kol (current slow)
             (cond ((null current) :proper)
                   ((atom current) :dotted)
                   ((null (cdr current)) :proper)
                   ((atom (cdr current)) :dotted)
                   ((eq current slow)    :circular)
                   (t (kol (cdr (cdr current)) (cdr slow))))))
    (if (null list)
        :empty
        (kol list (cons nil list)))))

(defmacro dotted-do ((var expression &optional result-form) &body body)
  (let ((current (gensym "current-")))
    `(do* ((,current ,expression (when (consp ,current) (cdr ,current)))
           (,var #1=(if (consp ,current) (car ,current) ,current) #1#))
          ((null ,current) ,result-form)
       ,@body)))

(defun meta-form (form free-variables)
  ;; FORM must not be a circular structure.
  (if (null free-variables)
      (list 'quote form)
      (cond ((symbolp form) (if (member form free-variables)
                                form
                                (list 'quote form)))
            ((atom form)        (list 'quote form))
            (t (let ((kind  (kind-of-list form)))
                 (ecase kind
                   (:circular (error "Cannot handle circular lists"))
                   (:empty    'nil)
                   ((:proper :dotted)
                    (cons (if (eq :proper kind) 'list 'list*)
                          (let ((result '()))
                            (dotted-do (subform  form  (nreverse result))
                                       (push (meta-form subform free-variables)
                                             result)))))))))))



C/USER[68]> (meta-form '(append
                         (list 'progn
                               (list 'format 't '"<~a>~%" (list 'quote sname)))
                         content
                         (list (list 'format 't '"</~a>~%" (list 'quote sname)))) 
                       '(sname))
(LIST 'APPEND
 (LIST 'LIST (LIST 'QUOTE 'PROGN)
  (LIST 'LIST (LIST 'QUOTE 'FORMAT) (LIST 'QUOTE 'T) (LIST 'QUOTE '"<~a>~%")
   (LIST 'LIST (LIST 'QUOTE 'QUOTE) SNAME)))
 'CONTENT
 (LIST 'LIST
  (LIST 'LIST (LIST 'QUOTE 'FORMAT) (LIST 'QUOTE 'T) (LIST 'QUOTE '"</~a>~%")
   (LIST 'LIST (LIST 'QUOTE 'QUOTE) SNAME))))


But honestly, this is less readable than embedded backquotes.
You only have to remember that the coma refer to the closest enclosing backquote.

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

Nobody can fix the economy.  Nobody can be trusted with their finger
on the button.  Nobody's perfect.  VOTE FOR NOBODY.
From: Sacha
Subject: Re: macro that writes macros
Date: 
Message-ID: <y7rgj.16677$VM7.2332879@phobos.telenet-ops.be>
Pascal Bourguignon wrote:
> Sacha <····@address.spam> writes:
> 
>> ·····················@gmail.com wrote:
>>> (defmacro new-tag (name)
>>>   (let ((sname (string-downcase (symbol-name name))))
>>>     `(defmacro ,name (&rest content)
>>>        `(progn
>>> 	  (format t "<~a>~%" ,',sname)
>>> 	  ,@content
>>> 	  (format t "</~a>~%" ,',sname)))))
>>>
>> This is the occasion for me to ask how to get rid of these ','symbol
>> things. While in this case it's pretty obvious how they work, it
>> sometimes make the code pretty hard to read and understand. I recently
>> came up with a very subtle bug (for me anyways) using such syntax.
>>
>> So what are you guys usually doing to avoid these ? Do you avoid these
>> at all ?
> 
> Don't use backquote.
> 
> 
> (defmacro new-tag (name)
>   (let ((sname (string-downcase (symbol-name name))))
>     (list 'defmacro name '(&rest content)
>           (list 'append
>                 (list 'list (list 'quote 'progn)
>                       (list 'list (list 'quote 'format) (list 'quote 't) (list 'quote '"<~a>~%")
>                             (list 'list (list 'quote 'quote) sname)))
>                 'content
>                 (list 'list
>                       (list 'list (list 'quote 'format) (list 'quote 't) (list 'quote '"</~a>~%")
>                             (list 'list (list 'quote 'quote) sname)))))))
> 
> 
> C/USER[67]> (macroexpand-1 '(new-tag div))
> (DEFMACRO DIV (&REST CONTENT)
>  (APPEND (LIST 'PROGN (LIST 'FORMAT 'T '"<~a>~%" (LIST 'QUOTE #1="div"))) CONTENT
>   (LIST (LIST 'FORMAT 'T '"</~a>~%" (LIST 'QUOTE #1#))))) ;
> T
>         
> 
> Note that to build these list expressions, this function may be handy:
> 
> (defun kind-of-list (list)
>   (labels ((kol (current slow)
>              (cond ((null current) :proper)
>                    ((atom current) :dotted)
>                    ((null (cdr current)) :proper)
>                    ((atom (cdr current)) :dotted)
>                    ((eq current slow)    :circular)
>                    (t (kol (cdr (cdr current)) (cdr slow))))))
>     (if (null list)
>         :empty
>         (kol list (cons nil list)))))
> 
> (defmacro dotted-do ((var expression &optional result-form) &body body)
>   (let ((current (gensym "current-")))
>     `(do* ((,current ,expression (when (consp ,current) (cdr ,current)))
>            (,var #1=(if (consp ,current) (car ,current) ,current) #1#))
>           ((null ,current) ,result-form)
>        ,@body)))
> 
> (defun meta-form (form free-variables)
>   ;; FORM must not be a circular structure.
>   (if (null free-variables)
>       (list 'quote form)
>       (cond ((symbolp form) (if (member form free-variables)
>                                 form
>                                 (list 'quote form)))
>             ((atom form)        (list 'quote form))
>             (t (let ((kind  (kind-of-list form)))
>                  (ecase kind
>                    (:circular (error "Cannot handle circular lists"))
>                    (:empty    'nil)
>                    ((:proper :dotted)
>                     (cons (if (eq :proper kind) 'list 'list*)
>                           (let ((result '()))
>                             (dotted-do (subform  form  (nreverse result))
>                                        (push (meta-form subform free-variables)
>                                              result)))))))))))
> 
> 
> 
> C/USER[68]> (meta-form '(append
>                          (list 'progn
>                                (list 'format 't '"<~a>~%" (list 'quote sname)))
>                          content
>                          (list (list 'format 't '"</~a>~%" (list 'quote sname)))) 
>                        '(sname))
> (LIST 'APPEND
>  (LIST 'LIST (LIST 'QUOTE 'PROGN)
>   (LIST 'LIST (LIST 'QUOTE 'FORMAT) (LIST 'QUOTE 'T) (LIST 'QUOTE '"<~a>~%")
>    (LIST 'LIST (LIST 'QUOTE 'QUOTE) SNAME)))
>  'CONTENT
>  (LIST 'LIST
>   (LIST 'LIST (LIST 'QUOTE 'FORMAT) (LIST 'QUOTE 'T) (LIST 'QUOTE '"</~a>~%")
>    (LIST 'LIST (LIST 'QUOTE 'QUOTE) SNAME))))
> 
> 
> But honestly, this is less readable than embedded backquotes.
> You only have to remember that the coma refer to the closest enclosing backquote.
> 

Sorry for the late follow-up ...
Yes, this is a whole less readable. I think I'll go for a better 
decomposition, maybe use higher order functions instead of macros too.

Sacha