From: Jeff Cunningham
Subject: How can this macro be improved?
Date: 
Message-ID: <pan.2005.05.22.01.17.07.553756@cunningham.net>
I (a neophyte at macros) wrote this as an exercise. The idea was to have
it write an html link with or without attributes. That is, one of the two
following ways:

<a href="somelink.html">Some text</a>

<a href="somelink.html" someproperty="123">Some text</a>

This is what I came up with:


(defmacro hlink (dest attributes-fcn &rest body-fcns)
  (let ((has-attributes (gensym)))
    `(let ((,has-attributes (length ',attributes-fcn)))
      (if (> ,has-attributes 0)
          (progn
            (format t "<a href=\"~A\" " ,dest)
            ,attributes-fcn
            (format t ">~%"))
          (format t "<a href=\"~A\">~%" ,dest))
      ,@body-fcns
      (format t "~&</a>")
      nil)))


Here are my test cases:

(hlink "index.html" nil (princ "Some text")) (hlink "index.html" (princ
"someproperty=\"123\"") (princ "Some text"))


How could I improve this?

Thanks.

--jeff cunningham

From: Rainer Joswig
Subject: Re: How can this macro be improved?
Date: 
Message-ID: <joswig-E5C97C.04153422052005@news-europe.giganews.com>
In article <······························@cunningham.net>,
 Jeff Cunningham <·······@cunningham.net> wrote:

> I (a neophyte at macros) wrote this as an exercise. The idea was to have
> it write an html link with or without attributes. That is, one of the two
> following ways:
> 
> <a href="somelink.html">Some text</a>
> 
> <a href="somelink.html" someproperty="123">Some text</a>
> 
> This is what I came up with:
> 
> 
> (defmacro hlink (dest attributes-fcn &rest body-fcns)
>   (let ((has-attributes (gensym)))
>     `(let ((,has-attributes (length ',attributes-fcn)))
>       (if (> ,has-attributes 0)
>           (progn
>             (format t "<a href=\"~A\" " ,dest)
>             ,attributes-fcn
>             (format t ">~%"))
>           (format t "<a href=\"~A\">~%" ,dest))
>       ,@body-fcns
>       (format t "~&</a>")
>       nil)))
> 
> 
> Here are my test cases:
> 
> (hlink "index.html" nil (princ "Some text")) (hlink "index.html" (princ
> "someproperty=\"123\"") (princ "Some text"))
> 
> 
> How could I improve this?
> 
> Thanks.
> 
> --jeff cunningham

What is this obsession with macros? You need a macro,
when you:

- want to do a really useful source transformation.
-- something where the macro make code more readable than a function.
-- the source transformation is non-trivial
- you want to add some compile-time side effects


First improvement: don't use a macro. use a function.


(defun hlink (link text-function attributes-function stream)
  (format stream "<a href=\"~A\"" link stream)
  (when (functionp attributes-function)
    (princ " " stream)
    (funcall attributes-function stream))
  (format stream ">")
  (funcall text-function stream)
  (format stream "</a>")
  stream)

(hlink "http://lispm.dyndns.org/"
       (lambda (stream)
         (princ "Rainer Joswig's Homepage" stream))
       nil
       *standard-output*)


Why would you use a macro for it?


It makes the code hard to debug
  (because in a debugger you won't see a stack frame for it).
It brings no obvious advantage.
It makes changes more difficult
  (because if you change the macro, you have
  to recompile all places where you use the macro).
It increases the compiled code size.

Summary don't use a macro. Never. Only when you
really know that you really NEED a macro. Then
try to replace the macro with a function. If
you fail, maybe the macro is needed.


Next improvement: use streams.

Next improvement: understand the various ways to do nice
argument lists in Lisp (optional, rest, keyword arguments).

Next improvement: check the wording of argument names.
A piece of code is not a "fcn".

Next improvement: use a descriptive documentation string, where you
describe the purpose of your function or macro, how to use it,
explain the arguments, the result and the side effects.


If you really want to write a macro, look first for an example, where
it makes sense to use a macro. Otherwise you already start in the
wrong direction.
From: ·····@rogers.com
Subject: Re: How can this macro be improved?
Date: 
Message-ID: <1116730427.592031.223570@g43g2000cwa.googlegroups.com>
Rainer Joswig wrote:
> In article <······························@cunningham.net>,
>  Jeff Cunningham <·······@cunningham.net> wrote:
>
> > I (a neophyte at macros) wrote this as an exercise. The idea was to
have
> > it write an html link with or without attributes. That is, one of
the two
> > following ways:
> >
> > <a href="somelink.html">Some text</a>
> >
> > <a href="somelink.html" someproperty="123">Some text</a>
> >
> > This is what I came up with:
> >
> >
> > (defmacro hlink (dest attributes-fcn &rest body-fcns)
> >   (let ((has-attributes (gensym)))
> >     `(let ((,has-attributes (length ',attributes-fcn)))
> >       (if (> ,has-attributes 0)
> >           (progn
> >             (format t "<a href=\"~A\" " ,dest)
> >             ,attributes-fcn
> >             (format t ">~%"))
> >           (format t "<a href=\"~A\">~%" ,dest))
> >       ,@body-fcns
> >       (format t "~&</a>")
> >       nil)))
> >
> >
> > Here are my test cases:
> >
> > (hlink "index.html" nil (princ "Some text")) (hlink "index.html"
(princ
> > "someproperty=\"123\"") (princ "Some text"))
> >
> >
> > How could I improve this?
> >
> > Thanks.
> >
> > --jeff cunningham
>
> What is this obsession with macros? You need a macro,
> when you:
>
> - want to do a really useful source transformation.
> -- something where the macro make code more readable than a function.
> -- the source transformation is non-trivial
> - you want to add some compile-time side effects
>
>
> First improvement: don't use a macro. use a function.
>
>
> (defun hlink (link text-function attributes-function stream)
>   (format stream "<a href=\"~A\"" link stream)
>   (when (functionp attributes-function)
>     (princ " " stream)
>     (funcall attributes-function stream))
>   (format stream ">")
>   (funcall text-function stream)
>   (format stream "</a>")
>   stream)
>
> (hlink "http://lispm.dyndns.org/"
>        (lambda (stream)
>          (princ "Rainer Joswig's Homepage" stream))
>        nil
>        *standard-output*)
>
>
> Why would you use a macro for it?
>
>
> It makes the code hard to debug
>   (because in a debugger you won't see a stack frame for it).
> It brings no obvious advantage.
> It makes changes more difficult
>   (because if you change the macro, you have
>   to recompile all places where you use the macro).
> It increases the compiled code size.
>
> Summary don't use a macro. Never. Only when you
> really know that you really NEED a macro. Then
> try to replace the macro with a function. If
> you fail, maybe the macro is needed.
>
>
> Next improvement: use streams.
>
> Next improvement: understand the various ways to do nice
> argument lists in Lisp (optional, rest, keyword arguments).
>
> Next improvement: check the wording of argument names.
> A piece of code is not a "fcn".
>
> Next improvement: use a descriptive documentation string, where you
> describe the purpose of your function or macro, how to use it,
> explain the arguments, the result and the side effects.
>
>
> If you really want to write a macro, look first for an example, where
> it makes sense to use a macro. Otherwise you already start in the
> wrong direction.

I love this forum for the CL language skills exhibited, but often stand
struck by the periodic lack of natural language skills.

The OP plainly stated he/she was "a neophyte at macros"; hence my
incredulity at a reply which includes rhetorical questions like "Why
would you use a macro for it?" or "What is this obsession with
macros?".

Is there a natural language mismapping going on here (ie. English is
not your first language while for me it is), or are you brow-beating
the OP?

(To Readers: Incidentally, is there a known Lisp study/library that
addresses semantic mappings between natural languages?)

p.s. Nevertheless, your advice about macros benefits me too.   8)
From: Tron3k
Subject: Re: How can this macro be improved?
Date: 
Message-ID: <1116735175.863462.311900@g43g2000cwa.googlegroups.com>
·····@rogers.com wrote:
> Rainer Joswig wrote:
> > In article <······························@cunningham.net>,
> >  Jeff Cunningham <·······@cunningham.net> wrote:
> >
> > > I (a neophyte at macros) wrote this as an exercise. The idea was
to
> have
> > > it write an html link with or without attributes. That is, one of
> the two
> > > following ways:
> > >
> > > <a href="somelink.html">Some text</a>
> > >
> > > <a href="somelink.html" someproperty="123">Some text</a>
> > >
> > > This is what I came up with:
> > >
> > >
> > > (defmacro hlink (dest attributes-fcn &rest body-fcns)
> > >   (let ((has-attributes (gensym)))
> > >     `(let ((,has-attributes (length ',attributes-fcn)))
> > >       (if (> ,has-attributes 0)
> > >           (progn
> > >             (format t "<a href=\"~A\" " ,dest)
> > >             ,attributes-fcn
> > >             (format t ">~%"))
> > >           (format t "<a href=\"~A\">~%" ,dest))
> > >       ,@body-fcns
> > >       (format t "~&</a>")
> > >       nil)))
> > >
> > >
> > > Here are my test cases:
> > >
> > > (hlink "index.html" nil (princ "Some text")) (hlink "index.html"
> (princ
> > > "someproperty=\"123\"") (princ "Some text"))
> > >
> > >
> > > How could I improve this?
> > >
> > > Thanks.
> > >
> > > --jeff cunningham
> >
> > What is this obsession with macros? You need a macro,
> > when you:
> >
> > - want to do a really useful source transformation.
> > -- something where the macro make code more readable than a
function.
> > -- the source transformation is non-trivial
> > - you want to add some compile-time side effects
> >
> >
> > First improvement: don't use a macro. use a function.
> >
> >
> > (defun hlink (link text-function attributes-function stream)
> >   (format stream "<a href=\"~A\"" link stream)
> >   (when (functionp attributes-function)
> >     (princ " " stream)
> >     (funcall attributes-function stream))
> >   (format stream ">")
> >   (funcall text-function stream)
> >   (format stream "</a>")
> >   stream)
> >
> > (hlink "http://lispm.dyndns.org/"
> >        (lambda (stream)
> >          (princ "Rainer Joswig's Homepage" stream))
> >        nil
> >        *standard-output*)
> >
> >
> > Why would you use a macro for it?
> >
> >
> > It makes the code hard to debug
> >   (because in a debugger you won't see a stack frame for it).
> > It brings no obvious advantage.
> > It makes changes more difficult
> >   (because if you change the macro, you have
> >   to recompile all places where you use the macro).
> > It increases the compiled code size.
> >
> > Summary don't use a macro. Never. Only when you
> > really know that you really NEED a macro. Then
> > try to replace the macro with a function. If
> > you fail, maybe the macro is needed.
> >
> >
> > Next improvement: use streams.
> >
> > Next improvement: understand the various ways to do nice
> > argument lists in Lisp (optional, rest, keyword arguments).
> >
> > Next improvement: check the wording of argument names.
> > A piece of code is not a "fcn".
> >
> > Next improvement: use a descriptive documentation string, where you
> > describe the purpose of your function or macro, how to use it,
> > explain the arguments, the result and the side effects.
> >
> >
> > If you really want to write a macro, look first for an example,
where
> > it makes sense to use a macro. Otherwise you already start in the
> > wrong direction.
>
> I love this forum for the CL language skills exhibited, but often
stand
> struck by the periodic lack of natural language skills.
>
> The OP plainly stated he/she was "a neophyte at macros"; hence my
> incredulity at a reply which includes rhetorical questions like "Why
> would you use a macro for it?" or "What is this obsession with
> macros?".
>
> Is there a natural language mismapping going on here (ie. English is
> not your first language while for me it is), or are you brow-beating
> the OP?
>
> (To Readers: Incidentally, is there a known Lisp study/library that
> addresses semantic mappings between natural languages?)
>
> p.s. Nevertheless, your advice about macros benefits me too.   8)

This is so true. Just today I saw someone throw out the acronym "MOP"
in responding to a newbie's post about CLOS. Well, Paul Graham did
write about hackers often being unable to see things from other
people's perspectives.
From: Kalle Olavi Niemitalo
Subject: expecting newbies to look things up (was: How can this macro be improved?)
Date: 
Message-ID: <87ekbz3fgi.fsf_-_@Astalo.kon.iki.fi>
"Tron3k" <······@gmail.com> writes:

> Just today I saw someone throw out the acronym "MOP"
> in responding to a newbie's post about CLOS.

That was in a list of CLOS features that the newbie would then
look up and consider learning.  I don't think expanding the
abbreviation would have helped much.  Admittedly, the glossary of
CLHS (and presumably the standard as well) includes an entry for
"Metaobject Protocol" but none for "MOP".  It's still easy to
find in the Web if qualified with "CLOS" or "Lisp".
From: Cor Gest
Subject: Re: How can this macro be improved?
Date: 
Message-ID: <874qcvvqe8.fsf@cleopatra.clsnet.nl>
·····@rogers.com wrote:
[vy big snip]
> 
> The OP plainly stated he/she was "a neophyte at macros"; hence my
> incredulity at a reply which includes rhetorical questions like "Why
> would you use a macro for it?" or "What is this obsession with
> macros?".
> 
> Is there a natural language mismapping going on here (ie. English is
> not your first language while for me it is), or are you brow-beating
> the OP?
> 
> (To Readers: Incidentally, is there a known Lisp study/library that
> addresses semantic mappings between natural languages?)

idiom is nearly unmappable and highly dependant on context and other
secundary and tertiary issues culture and circumstances.
 
> p.s. Nevertheless, your advice about macros benefits me too.   8)

Well it's just the proverbial value nightmare, best described in an
anecdotal fashion like below,   

farmer sits under tree next to his field contently smoking a pipe 
helper sees the field is full of weed
helper says : why not get rid of weed
farmer asks : why
helper says : get more yield
farmer asks : why
helper says : sell surplus 
farmer asks : why
helper says : get more money
farmer asks : why
helper says : buy more land 
farmer asks : why 
helper says : hire people to work your land 
farmer asks : why
helper says : you get rich
farmer asks : why
helper says : so you do not have to work the land 
farmer says : what do you think I am doing now ?

When one writes an dissertation one expects an academic answer,
otherwise one just expects an answer to the problem at hand.


Cor

-- 
To really make a mess of things one should use someoneelses computer
From: Jeff Cunningham
Subject: Re: How can this macro be improved?
Date: 
Message-ID: <pan.2005.05.22.04.16.53.893771@cunningham.net>
On Sun, 22 May 2005 04:15:34 +0200, Rainer Joswig wrote:

> In article <······························@cunningham.net>,
>  Jeff Cunningham <·······@cunningham.net> wrote:
> 
>> I (a neophyte at macros) wrote this as an exercise. The idea was to have
>> it write an html link with or without attributes. That is, one of the two
>> following ways:
>> 
>> <a href="somelink.html">Some text</a>
>> 
>> <a href="somelink.html" someproperty="123">Some text</a>
>> 
>> This is what I came up with:
>> 
>> 
>> (defmacro hlink (dest attributes-fcn &rest body-fcns)
>>   (let ((has-attributes (gensym)))
>>     `(let ((,has-attributes (length ',attributes-fcn)))
>>       (if (> ,has-attributes 0)
>>           (progn
>>             (format t "<a href=\"~A\" " ,dest)
>>             ,attributes-fcn
>>             (format t ">~%"))
>>           (format t "<a href=\"~A\">~%" ,dest))
>>       ,@body-fcns
>>       (format t "~&</a>")
>>       nil)))
>> 
>> 
>> Here are my test cases:
>> 
>> (hlink "index.html" nil (princ "Some text")) (hlink "index.html" (princ
>> "someproperty=\"123\"") (princ "Some text"))
>> 
>> 
>> How could I improve this?
>> 
>> Thanks.
>> 
>> --jeff cunningham
> 
> What is this obsession with macros? You need a macro,
> when you:
> 
> - want to do a really useful source transformation.
> -- something where the macro make code more readable than a function.
> -- the source transformation is non-trivial
> - you want to add some compile-time side effects
> 
> 
> First improvement: don't use a macro. use a function.
> 
> 
> (defun hlink (link text-function attributes-function stream)
>   (format stream "<a href=\"~A\"" link stream)
>   (when (functionp attributes-function)
>     (princ " " stream)
>     (funcall attributes-function stream))
>   (format stream ">")
>   (funcall text-function stream)
>   (format stream "</a>")
>   stream)
> 
> (hlink "http://lispm.dyndns.org/"
>        (lambda (stream)
>          (princ "Rainer Joswig's Homepage" stream))
>        nil
>        *standard-output*)
> 
> 
> Why would you use a macro for it?
> 
> 
> It makes the code hard to debug
>   (because in a debugger you won't see a stack frame for it).
> It brings no obvious advantage.
> It makes changes more difficult
>   (because if you change the macro, you have
>   to recompile all places where you use the macro).
> It increases the compiled code size.
> 
> Summary don't use a macro. Never. Only when you
> really know that you really NEED a macro. Then
> try to replace the macro with a function. If
> you fail, maybe the macro is needed.
> 
> 
> Next improvement: use streams.
> 
> Next improvement: understand the various ways to do nice
> argument lists in Lisp (optional, rest, keyword arguments).
> 
> Next improvement: check the wording of argument names.
> A piece of code is not a "fcn".
> 
> Next improvement: use a descriptive documentation string, where you
> describe the purpose of your function or macro, how to use it,
> explain the arguments, the result and the side effects.
> 
> 
> If you really want to write a macro, look first for an example, where
> it makes sense to use a macro. Otherwise you already start in the
> wrong direction.

Sorry if I offended you with the question. My primary reason for using a
macro is that I'm trying to learn how they work. I don't know enough
about them to be obsessed with them. The example is probably too trivial -
but its easier for me to understand it that way at this point. 

And actually, it was suggested to me by an even simplier macro given in
Paul Graham's book (Figure 16.5, page 262). So if its really so egregious,
go beat on him. 

I will take a look at your suggestions, because I want to understand your
thinking. But just prior to reading your post, I think I found an
improvement myself (still using a macro):

(defmacro hlink (dest attributes-fcn &rest body-fcn)
  (let ((spc (gensym)))
    (setf spc (not (null attributes-fcn)))
    `(progn
      (format t "<a href=\"~A\"~:[~; ~]" ,dest ,spc)
        ,attributes-fcn
        (format t ">~%")
        ,@body-fcn
        (format t "~&</a>"))))

Go ahead, pound away - I can take it. 

--jeff cunningham
From: Rainer Joswig
Subject: Re: How can this macro be improved?
Date: 
Message-ID: <joswig-022D5E.07032122052005@news-europe.giganews.com>
In article <······························@cunningham.net>,
 Jeff Cunningham <·······@cunningham.net> wrote:

> > If you really want to write a macro, look first for an example, where
> > it makes sense to use a macro. Otherwise you already start in the
> > wrong direction.
> 
> Sorry if I offended you with the question. My primary reason for using a
> macro is that I'm trying to learn how they work.

Okay, but you are using a wrong example.

> I don't know enough
> about them to be obsessed with them. The example is probably too trivial -

It is not trivial. It is going in the wrong direction.
If you (or others) will write code like that, it will
be harder to maintain for no good reason.

> but its easier for me to understand it that way at this point. 
> 
> And actually, it was suggested to me by an even simplier macro given in
> Paul Graham's book (Figure 16.5, page 262). So if its really so egregious,
> go beat on him. 

I do. You mean his "ANSI Common Lisp book". I've read that book.
I don't like his coding style that much. His other book "On Lisp"
is even more centered around macros.

If you ever have maintained macro-infected code, you may understand what
I mean. I have written my share of macros - and be sure, I've
fallen into the obvious and non-obvious traps quite often.
Especially 'macro newbies' need to understand when and for
what macro usage is useful. Paul Graham advocates a certain
coding style that I don't like.

> I will take a look at your suggestions, because I want to understand your
> thinking. But just prior to reading your post, I think I found an
> improvement myself (still using a macro):
> 
> (defmacro hlink (dest attributes-fcn &rest body-fcn)
>   (let ((spc (gensym)))
>     (setf spc (not (null attributes-fcn)))
>     `(progn
>       (format t "<a href=\"~A\"~:[~; ~]" ,dest ,spc)
>         ,attributes-fcn
>         (format t ">~%")
>         ,@body-fcn
>         (format t "~&</a>"))))
> 
> Go ahead, pound away - I can take it. 

It's not about pounding. You are on the wrong track with it.
Too bad that Graham's book has such example code.

If I look at above code, I'm already confused by a few things:

- "fcn" seems to indicate that it takes a function, but it doesn't.

- you have a local variable SPC, which you have set to some GENSYM,
  but in the next line you SETF it to something else?
  For what is the GENSYM value when you get rid od the value
  shortly after?


plus

- change &REST to &BODY
- put the parameters before the body into a list


(defmacro hlink ((dest &optional attributes-form) &body text-forms)
  " some documentation here ... "
  (let ((spc (not (null attributes-form))))
    `(progn
       ;; some documentation here explaining the FORMAT string
       (format t "<a href=\"~A\"~:[~; ~]" ,dest ,spc)
       ,attributes-form
       (format t ">~%")
       ,@text-forms
        (format t "~&</a>"))))

(macroexpand
 '(hlink ("http://lispm.dyndns.org" (princ "foo=t"))
    (princ "Rainer Joswig's Homepage")))

(hlink ("http://lispm.dyndns.org")
  (princ "Rainer Joswig's Homepage"))


But as I said, DON'T write macros for stuff like that.
Plus write code to check what the macro expands to and
to check what the resulting code does.


> 
> --jeff cunningham
From: Jeff Cunningham
Subject: Re: How can this macro be improved?
Date: 
Message-ID: <pan.2005.05.22.05.35.12.929094@cunningham.net>
On Sun, 22 May 2005 07:03:21 +0200, Rainer Joswig wrote:

> Okay, but you are using a wrong example. <snip>
> It is not trivial. It is going in the wrong direction. If you (or
> others) will write code like that, it will be harder to maintain for no
> good reason.

Okay, I'll accept that.


> - "fcn" seems to indicate that it takes a function, but it doesn't.
> 
> - you have a local variable SPC, which you have set to some GENSYM,
>   but in the next line you SETF it to something else? For what is the
>   GENSYM value when you get rid od the value shortly after?
> 
> plus
> 
> - change &REST to &BODY
> - put the parameters before the body into a list
> 
> 
> (defmacro hlink ((dest &optional attributes-form) &body text-forms)
>   " some documentation here ... "
>   (let ((spc (not (null attributes-form))))
>     `(progn
>        ;; some documentation here explaining the FORMAT string (format t
>        "<a href=\"~A\"~:[~; ~]" ,dest ,spc) ,attributes-form
>        (format t ">~%")
>        ,@text-forms
>         (format t "~&</a>"))))
> 
> (macroexpand
>  '(hlink ("http://lispm.dyndns.org" (princ "foo=t"))
>     (princ "Rainer Joswig's Homepage")))
> 
> (hlink ("http://lispm.dyndns.org")
>   (princ "Rainer Joswig's Homepage"))

Thank you very much for taking the time to explain this to me. It has been
very helpful.

--jeff cunningham
From: Frank Buss
Subject: Re: How can this macro be improved?
Date: 
Message-ID: <d6pt8v$77m$2@newsreader3.netcologne.de>
Jeff Cunningham <·······@cunningham.net> wrote:

> And actually, it was suggested to me by an even simplier macro given
> in Paul Graham's book (Figure 16.5, page 262). So if its really so
> egregious, go beat on him. 

Paul Graham's macro looks like this:

(defun html-file (base)
  (format nil "~(~A~).html" base))

(defmacro with-link (dest &rest body)
  `(progn
     (format t "<a href=\"~A\">" (html-file ,dest))
     ,@body
     (princ "</a>")))

and you can use it like this:

CL-USER > (with-link "page" (princ "Hello"))

<a href="page.html">Hello</a>

I think the reason for this macro was the automatic generation of the 
closing tag and that you have an implicit progn:

CL-USER > (with-link "page"
            (princ "Hello")
            (princ " World!"))

<a href="page.html">Hello World!</a>

But if you want to generate just links, this is too complicated and a 
simple function would be better:

(defun link (dest text)
  (format t "<a href=\"~A\">~A</a>" (html-file dest) text))

CL-USER > (link "page" "Hello")

<a href="page.html">Hello</a>

But if you want to create an embedded HTML language, you have to use 
macros, see my other thread about "HTML reader macro".

-- 
Frank Bu�, ··@frank-buss.de
http://www.frank-buss.de, http://www.it4-systems.de
From: Jeff Cunningham
Subject: Re: How can this macro be improved?
Date: 
Message-ID: <pan.2005.05.22.15.20.14.934424@cunningham.net>
On Sun, 22 May 2005 12:17:35 +0000, Frank Buss wrote:

> I think the reason for this macro was the automatic generation of the 
> closing tag and that you have an implicit progn:
> 
> CL-USER > (with-link "page"
>             (princ "Hello")
>             (princ " World!"))
> 
> <a href="page.html">Hello World!</a>
> 
> But if you want to generate just links, this is too complicated and a 
> simple function would be better:
> 
> (defun link (dest text)
>   (format t "<a href=\"~A\">~A</a>" (html-file dest) text))
> 
> CL-USER > (link "page" "Hello")
> 
> <a href="page.html">Hello</a>
> 
> But if you want to create an embedded HTML language, you have to use 
> macros, see my other thread about "HTML reader macro".


I did notice the value of the implicit progn. It seemed to me that it
would be messy to have that capability with function. But, as I mentioned
elsewhere in this thread, my primary purpose at this point is to learn
more about macros. Apparently I chose a bad example. I *am* working on an
embedded markup language project (but not html), so it seemed relevant
to me. 

I will find your other thread. 
Thank you. 

--jeff cunningham
From: Tim X
Subject: Re: How can this macro be improved?
Date: 
Message-ID: <87u0kvlth6.fsf@tiger.rapttech.com.au>
Rainer Joswig <······@lisp.de> writes:

>> This is what I came up with:
>> 
>> 
>> (defmacro hlink (dest attributes-fcn &rest body-fcns)
>>   (let ((has-attributes (gensym)))
>>     `(let ((,has-attributes (length ',attributes-fcn)))
>>       (if (> ,has-attributes 0)
>>           (progn
>>             (format t "<a href=\"~A\" " ,dest)
>>             ,attributes-fcn
>>             (format t ">~%"))
>>           (format t "<a href=\"~A\">~%" ,dest))
>>       ,@body-fcns
>>       (format t "~&</a>")
>>       nil)))
>> 
>> 
[snip]
>
> What is this obsession with macros? You need a macro,
> when you:
>
> - want to do a really useful source transformation.
> -- something where the macro make code more readable than a function.
> -- the source transformation is non-trivial
> - you want to add some compile-time side effects
>
>
> First improvement: don't use a macro. use a function.
[snip]
> Why would you use a macro for it?
>
>
> It makes the code hard to debug
>   (because in a debugger you won't see a stack frame for it).
> It brings no obvious advantage.
> It makes changes more difficult
>   (because if you change the macro, you have
>   to recompile all places where you use the macro).
> It increases the compiled code size.
>
> Summary don't use a macro. Never. Only when you
> really know that you really NEED a macro. Then
> try to replace the macro with a function. If
> you fail, maybe the macro is needed.
>
>
> Next improvement: use streams.
>
> Next improvement: understand the various ways to do nice
> argument lists in Lisp (optional, rest, keyword arguments).
>
> Next improvement: check the wording of argument names.
> A piece of code is not a "fcn".
>
> Next improvement: use a descriptive documentation string, where you
> describe the purpose of your function or macro, how to use it,
> explain the arguments, the result and the side effects.
>
>
> If you really want to write a macro, look first for an example, where
> it makes sense to use a macro. Otherwise you already start in the
> wrong direction.

Rainer,

Thanks for this post - I've only been loking at lisp for a short time
and lurking on this list picking up bits and pieces. One of the things
which had me a bit confused was the number of posts I've seen
concerning macros when I couldn't see the reason not to use just a
function - I thought there was something I was missing in my
understanding. Your post confirms what I was feeling i.e. that there
seems to be a lot of people a little too obsessed with macros and
using them where a simple function would be as good. 

However, I suspect the original poster has been experiencing a similar
problem to me and that is when starting with CL, its actually quite
difficult to find an original use for a macro which isn't artificially
contrived. My approach has been not to worry too much about macros
just yet - when I find somehting is getting overly complex using a
function, I'll look into a macro, but until then I'll settle for
reading posts like yours and other macro related posts and seeing if I
can understand exactly how they work before using macroexpand to see
what it really expands to.  

regards,

Tim

-- 
Tim Cross
The e-mail address on this message is FALSE (obviously!). My real e-mail is
to a company in Australia called rapttech and my login is tcross - if you 
really need to send mail, you should be able to work it out!
From: Alan Crowe
Subject: Re: How can this macro be improved?
Date: 
Message-ID: <86k6lrjhy6.fsf@cawtech.freeserve.co.uk>
Jeff Cunningham wrote: 

> I (a neophyte at macros) wrote this as an exercise. The
> idea was to have it write an html link with or without
> attributes. That is, one of the two following ways:
> 
> <a href="somelink.html">Some text</a>
>
> <a href="somelink.html" someproperty="123">Some text</a>
> 
> This is what I came up with:
>
>
> (defmacro hlink (dest attributes-fcn &rest body-fcns)
>   (let ((has-attributes (gensym)))
>     `(let ((,has-attributes (length ',attributes-fcn)))
>       (if (> ,has-attributes 0)
>           (progn
>             (format t "<a href=\"~A\" " ,dest)
>             ,attributes-fcn
>             (format t ">~%"))
>           (format t "<a href=\"~A\">~%" ,dest))
>       ,@body-fcns
>       (format t "~&</a>")
>       nil)))

Line 1 (defmacro hlink (dest attributes-fcn &rest body-fcns)

comments

hlink is crytic, use hyperlink or anchor or anchor-tag

dest is unspecific. you have two destinations in play, the
stream that the output is being written to and the page to
which you are linking. use dest-url

attributes-fcn has two problems. First fcn is cryptic.
The second problem can be seen if your look at the hyperspec
entry for mapcar

   mapcar function &rest lists+ => result-list

This really means function. One squares the numbers in a
list like this

(mapcar (lambda(x) (* x x)) list-of-numbers)

So using fcn as an abbreviation for function conveys the
impression that a call of hlink looks like

   (hlink (lambda() stuff ... ) .... )

Notice the link between the two problems. Creating and
expanding cryptic abbreviations is a distraction. If you
come up with a cool abbreviation you have no chance of
noticing that the word you are abbreviating was ill-chosen
in the first place.

&rest Go to the hyperspec, alphabetic list of symbols, and
spot the link for non-alphabitic at the end of the alphabet.
Click through &body to reach

     3.4.4 Macro Lambda Lists

     &body is identical in function to &rest, but it can be
     used to inform certain output-formatting and editing
     functions that the remainder of the form is treated as
     a body, and should be indented accordingly.

My context-creating macros usually start

(defmacro /name/ ((/context-stuff/) &body code))

and continue with ,@code somewhere inside.

Line 2: Paul Graham teaches gensym. So for example, your
macro expands as 

* (macroexpand-1 '(hlink url this=that (do-this)(do-that)))

(LET ((#:G851 (LENGTH 'THIS=THAT)))
  (IF (> #:G851 0)
      (PROGN (FORMAT T "<a href=\"~A\" " URL) THIS=THAT (FORMAT T ">~%"))
      (FORMAT T "<a href=\"~A\">~%" URL))
  (DO-THIS)
  (DO-THAT)
  (FORMAT T "~&</a>")
  NIL)
T

Notice the user hostile name G851. The implicit and
unintended message is that you don't want to bother looking
at your macroexpansion. I prefer to use make-symbol

(let ((one-name (make-symbol "ANOTHER-NAME")))
  ...

For example

(defmacro do-matrix ((variable matrix-form) &body code)
  (let ((,matrix-holder (make-symbol "MATRIX")))
    `(let ((,matrix-holder ,matrix-form)) ...)))

there is a matrix-form which I intend to be evaluated
exactly once, so I will need a variable to hold the result.
It is called matrix-holder in the template and MATRIX in the
macroexpansion. Plodding, I know, but it helps me keep
macroexpansion time and compile time and run time straight.

Line 2: has-attributes

Consider 

prompt> (hlink "index.html"
               (princ "someproperty=\"123\"")
	       (princ "another property=\"456\"")
	       (princ "Some text"))

<a href="index.html" someproperty="123">
another property="456"Some text
</a>

whoops, that is not what I expected. The name has-attributes
doesn't really fit. How about 2nd-attribute-flag?

Line 3: (length ',attributes-fcn)

Look at the macroexpansion that gets generated

(LET ((#:G853 (LENGTH '(PRINC "someproperty=\"123\"")))) ...

There is no need to compute the length of the form, taken
from the source file, at run time. Move the length inside
the comma

   `(let ((,has-attributes ,(length attributes-fcn)))

or

   `(let ((,2nd-attribute-flag ,(not (null attributes-fcn)))

Now the macro looks like this


(defmacro hlink (dest attributes-fcn &rest body-fcns)
  (let ((has-attributes (gensym)))
    `(let ((,has-attributes ,(not (null attributes-fcn))))
      (if ,has-attributes
          (progn
            (format t "<a href=\"~A\" " ,dest)
            ,attributes-fcn
            (format t ">~%"))
          (format t "<a href=\"~A\">~%" ,dest))
      ,@body-fcns
      (format t "~&</a>")
      nil)))

and expands to

(LET ((#:G875 T))
  (IF #:G875
      (PROGN
       (FORMAT T "<a href=\"~A\" " "index.html")
       (PRINC "someproperty=\"123\"")
       (FORMAT T ">~%"))
      (FORMAT T "<a href=\"~A\">~%" "index.html"))
  (PRINC "another property=\"456\"")
  (PRINC "Some text")
  (FORMAT T "~&</a>")
  NIL)

or

(LET ((#:G876 NIL))
  (IF #:G876
      (PROGN (FORMAT T "<a href=\"~A\" " "index.html") NIL (FORMAT T ">~%"))
      (FORMAT T "<a href=\"~A\">~%" "index.html"))
  (PRINC "another property=\"456\"")
  (PRINC "Some text")
  (FORMAT T "~&</a>")

which is a bit weird really. Obviously if needs
promoting to compile time by breaking up the format
statement in three separate statements with the middle one
being controlled by the if.

Consider this an exercise for the reader, I've got to go and
make dinner.

Don't be put off by my fussing over choice of variable
names. It is not that important. However, if you are posting
questions on comp.lang.lisp remember that readers are short
of time. If too much of that time goes on decoding cryptic
variable names they will run out of time to answer your
question and you will lose out.

Alan Crowe
Edinburgh
Scotland
From: Jeff Cunningham
Subject: Re: How can this macro be improved?
Date: 
Message-ID: <pan.2005.05.22.20.27.06.341660@cunningham.net>
On Sun, 22 May 2005 18:20:33 +0100, Alan Crowe wrote:

<snipped: incredible exposition of how to rewrite my macro and why>

Thank you, very much. I appreciate everything you had to say and the time
you took to say it. I will adjust my naming conventions and try to
reorient my thinking about writing macros accordingly.

Regards,

--jeff cunningham
From: Joerg Hoehle
Subject: Re: How can this macro be improved?
Date: 
Message-ID: <upsuqoeeg.fsf@users.sourceforge.net>
Alan Crowe <····@cawtech.freeserve.co.uk> writes:
> Line 2: Paul Graham teaches gensym.[...]
> * (macroexpand-1 '(hlink url this=that (do-this)(do-that)))
> (LET ((#:G851 (LENGTH 'THIS=THAT)))
>   (IF (> #:G851 0)

> Notice the user hostile name G851. The implicit and
> unintended message is that you don't want to bother looking
> at your macroexpansion. I prefer to use make-symbol

> (let ((one-name (make-symbol "ANOTHER-NAME")))
>   ...

(gensym "ANOTHER-NAME") works as well.


> (defmacro do-matrix ((variable matrix-form) &body code)
>   (let ((,matrix-holder (make-symbol "MATRIX")))
>     `(let ((,matrix-holder ,matrix-form)) ...)))
> there is a matrix-form which I intend to be evaluated
> exactly once, so I will need a variable to hold the result.

I would not recommend MAKE-SYMBOL with do-matrix, because it's
possible that do-matrix may be used in a nested context. Having two
identical looking yet distinct variables would be very hostile to the
user of MACROEXPAND.

In other words, I'd use make-symbol only in locally bound contexts,
where nesting is very unlikely to occur and all references to the
variable would be very close to each other. do-matrix is not a good
fit, hlink is a bit better suited for using make-symbol IMHO.

But I wonder why nobody suggested using
(if (> (length this-that) 0)
 -> (if this-that ...) ; avoid length when all you need is a null test!
instead of introducing an extra variable?

Regards,
	Jorg Hohle
Telekom/T-Systems Technology Center
From: Alan Crowe
Subject: Re: How can this macro be improved?
Date: 
Message-ID: <86k6ky1kj8.fsf@cawtech.freeserve.co.uk>
Joerg Hoehle worries about make-symbol:

> Alan Crowe <····@cawtech.freeserve.co.uk> writes:
> > Line 2: Paul Graham teaches gensym.[...]
> > * (macroexpand-1 '(hlink url this=that (do-this)(do-that)))
> > (LET ((#:G851 (LENGTH 'THIS=THAT)))
> >   (IF (> #:G851 0)
>
> > Notice the user hostile name G851. The implicit and
> > unintended message is that you don't want to bother looking
> > at your macroexpansion. I prefer to use make-symbol
>
> > (let ((one-name (make-symbol "ANOTHER-NAME")))
> >   ...
>
> (gensym "ANOTHER-NAME") works as well.
>
>
> > (defmacro do-matrix ((variable matrix-form) &body code)
> >   (let ((,matrix-holder (make-symbol "MATRIX")))
> >     `(let ((,matrix-holder ,matrix-form)) ...)))
> > there is a matrix-form which I intend to be evaluated
> > exactly once, so I will need a variable to hold the result.
>
> I would not recommend MAKE-SYMBOL with do-matrix, because it's
> possible that do-matrix may be used in a nested context. Having two
> identical looking yet distinct variables would be very hostile to the
> user of MACROEXPAND.

I used to worry about this too, but MACROEXPAND does less
than you think. Consider

  (defmacro double (form)
    (let ((value-holder (make-symbol "VALUE-OF-FORM")))
      `(let ((,value-holder ,form))
	 (list ,value-holder ,value-holder))))

(macroexpand '(double (+ 5 7)))
=> (LET ((#:VALUE-OF-FORM (+ 5 7)))
     (LIST #:VALUE-OF-FORM #:VALUE-OF-FORM))

(double (+ 5 7)) => (12 12)

So far, so good. Now lets try nesting the macro.

(double (double (+ 5 7))) => ((12 12) (12 12))

How did that work? Lets try expanding the macro:

(macroexpand '(double (double (+ 5 7))))
=> (LET ((#:VALUE-OF-FORM (DOUBLE (+ 5 7))))
     (LIST #:VALUE-OF-FORM #:VALUE-OF-FORM))

Macroexpand stops because LET is not a macro. We will not
see the inner expansions DOUBLE unless we use a walker. For
example 

(walker:macroexpand-all '(double (double (+ 5 7))))
=>
(LET ((#:VALUE-OF-FORM
       (LET ((#:VALUE-OF-FORM (+ 5 7)))
         (LIST #:VALUE-OF-FORM #:VALUE-OF-FORM))))
  (LIST #:VALUE-OF-FORM #:VALUE-OF-FORM))

I have written recursive macros, but I have never used the
particular form of recursion which would cause macroexpand
to do multiple rounds of expansion of a single macro.

One of the things that I like about macros is that they help
enormously with testing and debugging.

Consider code that walks a list two-by-two

  (defvar animals '(lion lioness stallion mare))
  (let (count)
    (defun build-ark () (setf count 0))
    (defun board-ark (animal)
      (format t "~&~A : ~A" (incf count) animal)))
  (build-ark)

  (loop for (male female . waiting-in-rain) on animals by #'cddr
	do (board-ark male)(board-ark female))

1 : LION
2 : LIONESS
3 : STALLION
4 : MARE
NIL

Well, that is OK, but the loop form is an invitation to
typos. If your code is going to be walking lists two by two
alot then you are better off writing a macro

  (defmacro do-list-two-by-two ((var1 var2 list-generating-form) &body code)
    (let ((list-holder (make-symbol "LIST")))
      `(let ((,list-holder ,list-generating-form))
	 (flet ((f (,var1 ,var2) ,@code))
	   (loop for (x y . z) on ,list-holder by #'cddr do (f x y))))))

so you can write

  (do-list-two-by-two (male female animals)
    (board-ark male)
    (board-ark female))

Now you have to think hard about the macroexpansion

(LET ((#:LIST ANIMALS))
  (FLET ((F (MALE FEMALE)
           (BOARD-ARK MALE)
           (BOARD-ARK FEMALE)))
    (LOOP FOR (X Y . Z) ON #:LIST BY #'CDDR DO (F X Y))))

Does #:LIST get between the body of code and its lexical
environment? Yes, but that's OK because I used a gensym.

Does F get between the body of code and its lexical
environment? No, I used flet not labels, so I'm OK.

Why do I think this is a big win? Three reasons.

1)One has done the "zero defects" thing for the rest of ones
  program. Crufty little mistakes such as forgetting the by
  #'cddr or saying by #'cdddr have been designed out. Once
  the macro is debugged those mistakes have gone for ever.

2)How do you check test coverage for a real program? The
  body of your loop does something complicated, appropriate
  to that stage of your computation. If you had made a
  mistake, such as by #'cdddr, would your test cases catch
  it? If your test cases don't catch it, how do you design
  one that does? You want that kind of quesion to just go away.

3)You can put special test code inside the macro call to
  stress test it, using your knowledge of how it is written.

  (flet ((f (thing)
	    (format t "~&Processing ~A in outer lexical environment."
		    thing)))
    (do-list-two-by-two (a b '(1 2 3 4))(f a)(f b)))

    Processing 1 in outer lexical environment.
    Processing 2 in outer lexical environment.
    Processing 3 in outer lexical environment.
    Processing 4 in outer lexical environment.

  Using flet instead of labels has actually worked.

So my vision of how to use macros is to exploit the fact
that one can put abitary code inside them to test the hell
out of them. This involves looking at the macroexpansion and
running through a check list of things that could go
wrong. So I really want a macro expandion I can read. My
plan is to get the macro rock solid, and then never look
inside it again. 

Coming back to the point of expanding nested macros and
encountering two distinct symbols with the same name, I am
aiming at never doing this. The nice names, inserted into
the code with make-symbol, are intended to be purely
transitional. Once the macro is fully debugged, it gets used
like a built-in and never questioned.

Alan Crowe
Edinburgh
Scotland
From: Joerg Hoehle
Subject: Re: How can this macro be improved?
Date: 
Message-ID: <umzpjk7jo.fsf@users.sourceforge.net>
Alan Crowe <····@cawtech.freeserve.co.uk> writes:
[...] thanks for your long explanation!

> We will not
> see the inner expansions DOUBLE unless we use a walker.

Actually, since I'm busy with the Iterate package, I happen to look at
the output of a walker quite often :-)

> Macroexpand stops because LET is not a macro.

Yes, but since macroexpands expands macros, it can lead to quite a few
rounds of expansions, possibly making equally-named macros appear. Not
every macro expands into a special-operator.

> Why do I think this is a big win? Three reasons.
> 1)One has done the "zero defects" thing for the rest of ones
>   program.
> 2)How do you check test coverage for a real program?
> 3)You can put special test code inside the macro call to
>   stress test it, using your knowledge of how it is written.

While I appreciate this design goals, I've come to dislike little
contol structure utiliities such as your do-list-two-by-two.

IMHO they make reading foreign code (which is what I happen to do
quite often) difficult (and I start to hate those little libraries
some people write and that you need to install in order to get their
code to work).

OTOH, I'm fond of domain specific languages. I very much appreciate
macros that belong to the problem domain. do-list-two-by-two does not
sound like raising the level of abstraction enough to warrant a macro
of its own.

> Once the macro is fully debugged, it gets used
> like a built-in and never questioned.

Certainly, small macros help with that, yet I'm sometimes fed up with
too many small macros.

All in all, it's a balance between usefulness, readability,
reliability etc.  For example, John Foderaro's if* macros also does
not sound like "raising the level of abstraction enough" yet my
feeling about is "well, why not -- it's quite readable".

Regards,
	Jorg Hohle
Telekom/T-Systems Technology Center
From: Rob Warnock
Subject: Re: How can this macro be improved?
Date: 
Message-ID: <B7qdnQ5S0NDx2wzfRVn-rA@speakeasy.net>
Jeff Cunningham  <·······@cunningham.net> wrote:
+---------------
| I (a neophyte at macros) wrote this as an exercise. The idea was to have
| it write an html link with or without attributes. That is, one of the two
| following ways:
|   <a href="somelink.html">Some text</a>
|   <a href="somelink.html" someproperty="123">Some text</a>
+---------------

In addition to the excellent advice others have given you on macro
style, let me add just this: "Don't invent when you can steal."
[...or re-use, whatever.]  This applies to "style" as well as
to actual "code". The more different styles you can study before
freezing your own style the better off your code will be in the
long run.

As for the present topic, using macros to create a small embedded
language for convenient writing of HTML is a much-explored topic in
the Common Lisp community. Besides what you saw in Paul Graham's
book, there are two whole chapters on the topic in Peter Seibel's
"Practical Common Lisp" [Ch.30-31], as well as a plethora of
HTML-generating libraries available. See <http://www.cliki.net/Web>
for a few of them [and some other useful web tools].

Personally, I like HTOUT and CL-WHO, which let you write things
like this:

    > (let ((title "Simple Page") 
            (links '("foo" "bar" "baz")) 
            (link-url "http://gorp.com/"))
        (with-output-to-string (some-stream)
          (with-html-output (s some-stream)
            (:html
              (:head (:title title))
              (lfd) 
              (:body
                (:h1 title) (lfd)
                (loop for link in links 
                      for url = (concatenate 'string link-url link)
                  do (htm ((:a :href url) link)
                          :br (lfd))))))))

    "<HTML><HEAD><TITLE>Simple Page</TITLE></HEAD>
    <BODY><H1>Simple Page</H1>
    <A HREF='http://gorp.com/foo'>foo</A><BR>
    <A HREF='http://gorp.com/bar'>bar</A><BR>
    <A HREF='http://gorp.com/baz'>baz</A><BR>
    </BODY></HTML>"
    >

Notice how the embedded sub-language [s-exprs starting with keywords or,
for attributes, starting with lists starting with keywords] lets you
concentrate on the *shape* of the resulting page as a whole, rather
than on the mechanics of generating the individual HTML elements.

If you study HTOUT and CL-WHO plus Peter's chapters you'll get a
good set of alternate styles on the HTML-generating problem. Peter's
FOO in particular addresses the issue of nicely-formatting the
resulting HTML (or not), which HTOUT doesn't help much with, hence
the occasional explicit LFD (linefeed) calls in the above example.  ;-}

[Though looking at how the HTM, LFD, FMT, etc., macrolets get
defined in HTOUT is quite useful for pedagogical purposes...]


-Rob

-----
Rob Warnock			<····@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607