From: Ted Sandler
Subject: when are macros useful?
Date: 
Message-ID: <3B3EB873.22656A95@worldnet.att.net>
Hi, I've read that lisp macros enable one to do things "undoable" in
most other languages.  I was wondering what some examples of these
undoable things are, and how they would be implemented using macros.
-ted


-- 
··········@att.net

From: Christopher Stacy
Subject: Re: when are macros useful?
Date: 
Message-ID: <uy9q95ffc.fsf@spacy.Boston.MA.US>
>>>>> On Sun, 01 Jul 2001 05:43:29 GMT, Ted Sandler ("Ted") writes:
 Ted> Hi, I've read that lisp macros enable one to do things "undoable" in
 Ted> most other languages.  I was wondering what some examples of these
 Ted> undoable things are, and how they would be implemented using macros.

I don't know about claiming things to be "impossible" in general,
but Lisp does have an extremely powerful macro facility that is
part of the language definition.  Lisp macros are generally used to
extend the Lisp language as needed.

The main thing to know is that Lisp macros are written in Lisp:
they can therefore use the full power of Lisp to process the macro
arguments and generate the code for the macro's expansion.
This also means that the macro writer has full control over 
the syntax of the macro.
From: Kaz Kylheku
Subject: Re: when are macros useful?
Date: 
Message-ID: <oU637.679316$166.13983744@news1.rdc1.bc.home.com>
In article <·············@spacy.Boston.MA.US>, Christopher Stacy wrote:
>The main thing to know is that Lisp macros are written in Lisp:
>they can therefore use the full power of Lisp to process the macro
>arguments and generate the code for the macro's expansion.
>This also means that the macro writer has full control over 
>the syntax of the macro.

No, the macro invocation always looks like

	(my-macro-name arguments)

LISP has control over the syntax, the writer has control over
the shape of the trees involved. ;)
From: Kent M Pitman
Subject: Re: when are macros useful?
Date: 
Message-ID: <sfwsng2nch2.fsf@world.std.com>
···@ashi.footprints.net (Kaz Kylheku) writes:

> In article <·············@spacy.Boston.MA.US>, Christopher Stacy wrote:
> >The main thing to know is that Lisp macros are written in Lisp:
> >they can therefore use the full power of Lisp to process the macro
> >arguments and generate the code for the macro's expansion.
> >This also means that the macro writer has full control over 
> >the syntax of the macro.
> 
> No, the macro invocation always looks like
> 
> 	(my-macro-name arguments)
> 
> LISP has control over the syntax, the writer has control over
> the shape of the trees involved. ;)

Kaz, I don't see what the "No" here means.
How does what you said differe from what Chris said?
From: Kaz Kylheku
Subject: Re: when are macros useful?
Date: 
Message-ID: <55837.679453$166.13998681@news1.rdc1.bc.home.com>
In article <···············@world.std.com>, Kent M Pitman wrote:
>···@ashi.footprints.net (Kaz Kylheku) writes:
>
>> In article <·············@spacy.Boston.MA.US>, Christopher Stacy wrote:
>> >The main thing to know is that Lisp macros are written in Lisp:
>> >they can therefore use the full power of Lisp to process the macro
>> >arguments and generate the code for the macro's expansion.
>> >This also means that the macro writer has full control over 
>> >the syntax of the macro.
>> 
>> No, the macro invocation always looks like
>> 
>> 	(my-macro-name arguments)
>> 
>> LISP has control over the syntax, the writer has control over
>> the shape of the trees involved. ;)
>
>Kaz, I don't see what the "No" here means.
>How does what you said differe from what Chris said?

It depends on what you mean by syntax: whether you think of the
configuration of trees as syntax, or whether you think of the
actual form of the program text as syntax. I think
of the trees as internal representation, which has
a rigid mapping to an outside syntax imposed by LISP
(which is a good thing). To me, syntax is actually how
the input characters of the source program combine into
tokens, and tokens into constructs.

If you had full control over the the macro syntax, you
should be able to make it callable as (for instance):

	[ arg1 | arg2 | arg3 ] mymacro

So clearly, you don't have full control over syntax.
For that, you have to write a full blown parser.
From: Kent M Pitman
Subject: Re: when are macros useful?
Date: 
Message-ID: <sfw4rsihlxk.fsf@world.std.com>
···@ashi.footprints.net (Kaz Kylheku) writes:

> In article <···············@world.std.com>, Kent M Pitman wrote:
> >···@ashi.footprints.net (Kaz Kylheku) writes:
> >
> >> In article <·············@spacy.Boston.MA.US>, Christopher Stacy wrote:
> >> >The main thing to know is that Lisp macros are written in Lisp:
> >> >they can therefore use the full power of Lisp to process the macro
> >> >arguments and generate the code for the macro's expansion.
> >> >This also means that the macro writer has full control over 
> >> >the syntax of the macro.
> >> 
> >> No, the macro invocation always looks like
> >> 
> >> 	(my-macro-name arguments)
> >> 
> >> LISP has control over the syntax, the writer has control over
> >> the shape of the trees involved. ;)
> >
> >Kaz, I don't see what the "No" here means.
> >How does what you said differe from what Chris said?
> 
> It depends on what you mean by syntax: whether you think of the
> configuration of trees as syntax, or whether you think of the
> actual form of the program text as syntax.

Chris has been programming in Lisp for nearly as long as I have, and I
know he's not confused on that point.  That's why I asked.  You're just
misunderstanding his use of the term "syntax".

I also use the term "syntax" to mean "tree structure".

So does the ANSI CL spec.

> I think
> of the trees as internal representation, which has
> a rigid mapping to an outside syntax imposed by LISP
> (which is a good thing).

Well, one can cons a tree that has no syntax at all by that definition
of syntax.  For this reason, I always encourage people to understand
that Lisp "syntax" is defined on objects, not on text.

> To me, syntax is actually how
> the input characters of the source program combine into
> tokens, and tokens into constructs.

This is fine except it's inappropriate to think the term "syntax" as
uniquely implying this interpretation.  The normal use of syntax
in the community I deal in is "expression syntax", i.e., tree shape.

We usually refer to what you call syntax as "READ syntax" or
"reader syntax", but mostly we just assume that any attempt to refer
to text syntax is mere metaphor for the underlying text.  Most code
is indeed typed in, but when one asks "What is the syntax of with-open-file?"
one is *not* generally asking where the whitespace goes, and to the extent
one is asking where the parens go, one is generally using that as a 
convenient notational metaphor for asking where the list groupings are.

> If you had full control over the the macro syntax, you
> should be able to make it callable as (for instance):
> 
> 	[ arg1 | arg2 | arg3 ] mymacro

Again, I'd encourage you to use the term "read syntax" here.

There IS a legitimate expression syntax issue here as well, which is
what I thought you might be referring to (but apparently not) and which
did come up in the InterLisp dialect.  The question of whether you can
write [expression] syntax like

   (x * y)

and have the symbol * get control.  That's also something you can't do
in Lisp, so it's technically true even at the expression level that 
macros aren't totally in control of their syntax.  But even there, we 
as language designers rationalize this by saying there is an overarching
desire to have a language whose syntax has certain commonalities, and macros
do have the ability to grab control of the expression syntax to the extent
that any part of the language, even builtins, do.  That is, no system-provided
operator can do that either.

> So clearly, you don't have full control over syntax.
> For that, you have to write a full blown parser.

What you say, given your choice of meaning for syntax, is not wrong.
However, you're likely to confuse a lot of Lisp programmers if you insist
that this unqualified usage of the word syntax uniquely means your own
meaning.  I recommend using the term "read[er] syntax" to make your
meaning clear and avoid a lot of interpersonal confusion.
From: Christopher Stacy
Subject: Re: when are macros useful?
Date: 
Message-ID: <u8zhux0fz.fsf@spacy.Boston.MA.US>
>>>>> On Thu, 12 Jul 2001 02:38:57 GMT, Kaz Kylheku ("Kaz") writes:
 >>> No, the macro invocation always looks like
 >>> (my-macro-name arguments)

 Kaz> If you had full control over the the macro syntax, you
 Kaz> should be able to make it callable as (for instance):
 Kaz> 	[ arg1 | arg2 | arg3 ] mymacro
 Kaz> So clearly, you don't have full control over syntax.
 Kaz> For that, you have to write a full blown parser.

Your statement is very misleading in the context of the original
question, because with macros, the thing that you are calling
"arguments" are not evaluated according to the normal rules of
Lisp function calls.  Macros do not need to have the shape that
your example shows, except in the sense that a macro invocation
looks like a list with the name in the CAR.  (Also, unlike your
example, those "arguments" need not be a single thing --
additional syntax can be invented, such as in the LOOP macro)
Don't forget that you can also define reader-macros, which are
"full blown parsers" that are invoked by a single character,
and needn't look like a list at all.
From: Joshua Gooding
Subject: Re: when are macros useful?
Date: 
Message-ID: <99eb5551.0107151239.648e9dc@posting.google.com>
> If you had full control over the the macro syntax, you
> should be able to make it callable as (for instance):
> 
> 	[ arg1 | arg2 | arg3 ] mymacro
> 
> So clearly, you don't have full control over syntax.
> For that, you have to write a full blown parser.

Can't you do that with reader macros?
From: Kent M Pitman
Subject: Re: when are macros useful?
Date: 
Message-ID: <sfw8zhpg9m5.fsf@world.std.com>
·········@hotmail.com (Joshua Gooding) writes:

> 
> > If you had full control over the the macro syntax, you
> > should be able to make it callable as (for instance):
> > 
> > 	[ arg1 | arg2 | arg3 ] mymacro
> > 
> > So clearly, you don't have full control over syntax.
> > For that, you have to write a full blown parser.
> 
> Can't you do that with reader macros?

Not in the general case of trying to mix Lisp code and other code.
For readmacros to get a foothold, you must have a piece  of syntax
that introduces the special syntax and you must know when you are
released from it.  So, you can make a syntax like:


 & arg1 arg2 arg3 $ fn

or something like that where it reads forms up to a $ and then one form
after, constructing (fn arg1 arg2 arg3).  But you can't just have

 1 2 3 +

like in PostScript if you expect to mix it with Lisp, because what
would you do in the case of

 (- 1 2 3 +)

Would that mean

 (- (+ 1 2 3))

or

 (- 1 (+ 2 3))

or

 (- 1 2 (+ 3))

or

 (- 1 2 3 (+))

The Lisp Machine, for example, used a funny non-ascii character I'll
represent as $ here to do:

 #$ a*b-c*d $

So you could switch in and out of infix mode.  You could do similarly 
for some sort of RPN notation if you wanted.  But the thing you can't
do is just say that a given operator reads its arguments backwards 
because by the time you encounter the operator, you've already disposed
of the things to its left.
From: Marco Antoniotti
Subject: Re: when are macros useful?
Date: 
Message-ID: <y6cn165x9ht.fsf@octagon.mrl.nyu.edu>
Kent M Pitman <······@world.std.com> writes:

> ·········@hotmail.com (Joshua Gooding) writes:
> 
> > 
> > > If you had full control over the the macro syntax, you
> > > should be able to make it callable as (for instance):
> > > 
> > > 	[ arg1 | arg2 | arg3 ] mymacro
> > > 
> > > So clearly, you don't have full control over syntax.
> > > For that, you have to write a full blown parser.
> > 
> > Can't you do that with reader macros?
> 
> Not in the general case of trying to mix Lisp code and other code.
> For readmacros to get a foothold, you must have a piece  of syntax
> that introduces the special syntax and you must know when you are
> released from it.  So, you can make a syntax like:
> 
> 
>  & arg1 arg2 arg3 $ fn
> 
> or something like that where it reads forms up to a $ and then one form
> after, constructing (fn arg1 arg2 arg3).  But you can't just have
> 
>  1 2 3 +
> 
> like in PostScript if you expect to mix it with Lisp, because what
> would you do in the case of
> 
>  (- 1 2 3 +)
> 
> Would that mean
> 
>  (- (+ 1 2 3))
> 
> or
> 
>  (- 1 (+ 2 3))
> 
> or
> 
>  (- 1 2 (+ 3))
> 
> or
> 
>  (- 1 2 3 (+))
> 
> The Lisp Machine, for example, used a funny non-ascii character I'll
> represent as $ here to do:
> 
>  #$ a*b-c*d $
> 
> So you could switch in and out of infix mode.  You could do similarly 
> for some sort of RPN notation if you wanted.  But the thing you can't
> do is just say that a given operator reads its arguments backwards 
> because by the time you encounter the operator, you've already disposed
> of the things to its left.

The INFIX package available in the AI.Repository (and somewhere else,
I suppose) allows you to do

	#I( a*b-c*d / sin(x) )

and more.  THe original version used #\$ as delimiters (a
la' TeX), but that conflicted with a use of #\$ in MCL, so it was
changed to #I().

Cheers

-- 
Marco Antoniotti ========================================================
NYU Courant Bioinformatics Group        tel. +1 - 212 - 998 3488
719 Broadway 12th Floor                 fax  +1 - 212 - 995 4122
New York, NY 10003, USA                 http://bioinformatics.cat.nyu.edu
                    "Hello New York! We'll do what we can!"
                           Bill Murray in `Ghostbusters'.
From: Peter Wood
Subject: Re: when are macros useful?
Date: 
Message-ID: <80zoa4u86v.fsf@localhost.localdomain>
Kent M Pitman <······@world.std.com> writes:

> ·········@hotmail.com (Joshua Gooding) writes:
> 
> > 
> > > If you had full control over the the macro syntax, you
> > > should be able to make it callable as (for instance):
> > > 
> > > 	[ arg1 | arg2 | arg3 ] mymacro
> > > 
> > > So clearly, you don't have full control over syntax.
> > > For that, you have to write a full blown parser.
> > 
> > Can't you do that with reader macros?
> 
> Not in the general case of trying to mix Lisp code and other code.
> For readmacros to get a foothold, you must have a piece  of syntax
> that introduces the special syntax and you must know when you are
> released from it.  So, you can make a syntax like:
> 
> 
>  & arg1 arg2 arg3 $ fn
> 
> or something like that where it reads forms up to a $ and then one form
> after, constructing (fn arg1 arg2 arg3).  But you can't just have
> 
>  1 2 3 +

You can't have 1 2 3 +, but you can have #[1 2 3 +].

> 
> like in PostScript if you expect to mix it with Lisp, because what
> would you do in the case of
> 
>  (- 1 2 3 +)
> 

It's an error(using my read-macro). An argument to plus must be a number.
It would give an error.

> 
> So you could switch in and out of infix mode.  You could do similarly 
> for some sort of RPN notation if you wanted.  But the thing you can't
> do is just say that a given operator reads its arguments backwards 
> because by the time you encounter the operator, you've already disposed
> of the things to its left.

I am probably not understanding you, because this is easy to do:

(set-macro-character #\] (get-macro-character #\)))

(set-dispatch-macro-character #\# #\[
  (lambda (stream char1 char2)
    (declare (ignore char1 char2))
    (let* ((command-line (read-delimited-list #\] stream t))
	   (last (car (last command-line))))
      (cond ((equal last '+) (reverse command-line))
	    (t command-line)))))

[2]> #[1 2 3 +]
6
[3]> #[- 7 #[1 2 3 +]]
1
[4]> #[list #[- 7 #[1 2 3 +]]]
(1)
[5]> #[- 1 2 3 +]

*** - argument to + should be a number: (+ 3 2 1 -)
1. Break [6]> (quit)

Process shell finished

Regards,
Peter      
    
From: Kent M Pitman
Subject: Re: when are macros useful?
Date: 
Message-ID: <sfw4rsc7q2s.fsf@world.std.com>
Peter Wood <··········@worldonline.dk> writes:

> Kent M Pitman <······@world.std.com> writes:
> 
> > ·········@hotmail.com (Joshua Gooding) writes:
> > 
> > > Can't you do that with reader macros?
> > 
> > Not in the general case of trying to mix Lisp code and other code.
    ^^^^^^^^^^^^^^^^^^^^^^^
> > For readmacros to get a foothold, you must have a piece  of syntax
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> > that introduces the special syntax and you must know when you are
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> > released from it.  So, you can make a syntax like:
    ^^^^^^^^^^^^^^^^
> > 
> >  & arg1 arg2 arg3 $ fn
> > 
> > or something like that where it reads forms up to a $ and then one form
> > after, constructing (fn arg1 arg2 arg3).  But you can't just have
> > 
> >  1 2 3 +
> 
> You can't have 1 2 3 +, but you can have #[1 2 3 +].

You didn't read what I said.  Your "#[" is identical to my "&" above.
Your "]" is identical to my "$" above, except you put the opertor on the
other side.  

> > 
> > like in PostScript if you expect to mix it with Lisp, because what
> > would you do in the case of
> > 
> >  (- 1 2 3 +)
> > 
> 
> It's an error(using my read-macro). An argument to plus must be a number.
> It would give an error.

> > 
> > So you could switch in and out of infix mode.  You could do similarly 
> > for some sort of RPN notation if you wanted.  But the thing you can't
> > do is just say that a given operator reads its arguments backwards 
> > because by the time you encounter the operator, you've already disposed
> > of the things to its left.
> 
> I am probably not understanding you, because this is easy to do:

By "backwards" I mean ``before the "#["'', i.e., "before the special
syntax that you want to extend Lisp to have".  Your readmacro can't do
that either.  What other meaning of "backwards" are you assuming I'm 
meaning?

> (set-macro-character #\] (get-macro-character #\)))
> 
> (set-dispatch-macro-character #\# #\[
>   (lambda (stream char1 char2)
>     (declare (ignore char1 char2))
>     (let* ((command-line (read-delimited-list #\] stream t))
> 	   (last (car (last command-line))))
>       (cond ((equal last '+) (reverse command-line))
> 	    (t command-line)))))
> 
> [2]> #[1 2 3 +]
> 6
> [3]> #[- 7 #[1 2 3 +]]
> 1
> [4]> #[list #[- 7 #[1 2 3 +]]]
> (1)
> [5]> #[- 1 2 3 +]
> 
> *** - argument to + should be a number: (+ 3 2 1 -)
> 1. Break [6]> (quit)
> 
From: Peter Wood
Subject: Re: when are macros useful?
Date: 
Message-ID: <803d7uyck0.fsf@localhost.localdomain>
Kent M Pitman <······@world.std.com> writes:

> Peter Wood <··········@worldonline.dk> writes:
> 
> > Kent M Pitman <······@world.std.com> writes:
> > 
> > > ·········@hotmail.com (Joshua Gooding) writes:
> > > 
> > > > Can't you do that with reader macros?

> You didn't read what I said.  Your "#[" is identical to my "&" above.
> Your "]" is identical to my "$" above, except you put the opertor on the
> other side.  

Yes, sorry.  Your post was crystal clear, and my feeble excuse is that
I read it in a hurry.  I broke my own rule, that if I encounter
something which is unusually surprising, I should go back and check my
steps in arriving at the surprise, because things are usually _not_
surprising.

Regards,
Peter
From: Erik Naggum
Subject: Re: when are macros useful?
Date: 
Message-ID: <3202966596699145@naggum.net>
* Ted Sandler <··········@worldnet.att.net>
> Hi, I've read that lisp macros enable one to do things "undoable" in
> most other languages.  I was wondering what some examples of these
> undoable things are, and how they would be implemented using macros.

  Macros can change the syntax of the language or implement a sub-language,
  both of which are translated into the regular language before compilation
  and/or evaluation.  This is due to the lack of structure to Lisp forms,
  the flexibility in evaluation rules, and the uniformity of representation
  of language elements.

#:Erik
-- 
  Travel is a meat thing.
From: Kurt B. Kaiser
Subject: Re: when are macros useful?
Date: 
Message-ID: <m3r8w060mr.fsf@float.ne.mediaone.com>
Ted Sandler <··········@worldnet.att.net> writes:

> Hi, I've read that lisp macros enable one to do things "undoable" in
> most other languages.  I was wondering what some examples of these
> undoable things are, and how they would be implemented using macros.
> -ted
> 
> 
> -- 
> ··········@att.net

You might look at www.norvig.com

There is a link to "Tutorial on Good Lisp Programming Style"
by Peter Norvig and Kent Pitman

It has a number of macro examples.

(page 81 has the comment "Don't use a macro where a function would suffice.")

Regards, KBK
From: Peter Wood
Subject: Re: when are macros useful?
Date: 
Message-ID: <80r8w031vr.fsf@localhost.localdomain>
Ted Sandler <··········@worldnet.att.net> writes:

> Hi, I've read that lisp macros enable one to do things "undoable" in
> most other languages.  I was wondering what some examples of these
> undoable things are, and how they would be implemented using macros.
> -ted

I don't think anything is "undoable" in any language, but some things
are so difficult they just don't get done.

I also don't think it's possible to produce a Common Lisp macro and
say, "There, you see, _no_ other language can do _that_".

If you want to learn about CL macros you should try to get "On Lisp"
by Paul Graham.  Amazon.co.uk had copies last time I looked.  It is
packed with examples of macros which would be difficult or _nigh_
impossible to write any other way.

You can get the code from the book by ftp at

  ftp://ftp.das.harvard.edu/pub/onlisp

Regards,
Peter
From: Kaz Kylheku
Subject: Re: when are macros useful?
Date: 
Message-ID: <GW737.679431$166.13998716@news1.rdc1.bc.home.com>
In article <·················@worldnet.att.net>, Ted Sandler wrote:
>Hi, I've read that lisp macros enable one to do things "undoable" in
>most other languages.  I was wondering what some examples of these
>undoable things are, and how they would be implemented using macros.

An example of the power of LISP macros is the Common LISP Object System
(CLOS); an entire object oriented programming system that can be
implemented using macros. It polymorphism through generic functions,
multiple inheritance, and implementations of it even support meta-class
programming.   The (defclass ...) construct of CLOS is (implemented as) a
macro for defining a class.  Consider that when LISP macros were invented,
OOP didn't exist.  When OOP came along, entire programming language
families hit a brick wall and died. LISP programmers just wrote OOP
systems in LISP and merrily carried on. This inspires a kind of confidence
in LISP that it will be able to acquire any language feature that 
anyone comes up with in the forseeable future.

LISP programs execute by the evaluation of forms, which you can think
of as trees. Every language feature is some kind of form: function
definitions, loops and so on.

Most programming languages parse constructs and turn them 
into some kind of internal tree structure which is evaluated
(interpreter mode) or processed to produce a translation.

In LISP, you basically write these trees directly, thanks
to its minimal syntax. And these trees can be also treated
as data; so there is a blur in the systemic distinction between
code and data.

Macros are procedures which, at run time, construct trees
which are then evaluated.

When you call an ordinary LISP function, all of the arguments are
evaluated, and then the functions's parameters are bound to the resulting
values. When the function is done, it produces some value, or multiple
values.

Some built-in forms are special, in that they don't evaluate 
their parmeters. For example, a form for declaring a variable
obviously cannot evaluate the variable name being declared.
Or a form which iterates over some items, an on each iteration
binds a name to each item in turn, also cannot evaluate that
name.

Unlike functions, LISP macros allow you to create your own forms that
behave just like special forms. So you can introduce new language
constructs. If you want a new kind of loop you can create a macro which
provides it, completely seamlessly.

Macros receive their parameters unevaluated, and compute a form,
usually by carrying out some computation and then substituting
parameters into a template. The constructed form is returned and
then evaluated. Because this is done at run time, there is considerable
power and flexibility in what macros can do. The same macro can compute
something different each time; slightly different or even radically
different. For example, suppose that your newly invented language
construct needs to be able to invent a symbol for internal use. When
invoked, your macro can use the LISP (gensym) function to generate a
unique new symbol, which you can stick into appropriate places in the
returned form.

One feature of macros that helps them seamlessly emulate built-in forms
is called destructuring. Rather than taking only flat parameter lists,
macros can take nested parameter lists, so that

	(mymacro (a (b c (d e)) f))

can be a valid macro call in which the arguments a, b, c, d, ... 
are mapped to parameters. Thus macros can emulate language
features that have this type of rich, nested syntax. Within each
parameter nesting, you can have all the bells and whistles
of a full LISP parameter list: optional parameters, keyword
parameters and all that.

Here is a very simple macro example which shows some of the features:
destructuring, substituting into a template to produce a form,
using (gensym) to invent a needed symbol. The purpose of this macro
is to introduce a very simple language feature; a loop construct
which executes a sequence of forms N times, where N is a parameter.
We implement our new kind of loop, which we will call ``ntimes''
in terms of the Common LISP ``dotimes' loop:

    (defmacro ntimes (count . forms) (let ((counter (gensym)))
                                       `(dotimes
                                           (,counter ,count) ,@forms)))


The special dot syntax in the macro parameter list means that the
first argument to the macro will be known as ``count'', and ``forms''
will represent the entire list of remaining arguments.  Of course, all
the arguments are unevaluated, which is important because the forms are
program statements that we want to execute in the loop, and they need
to be incorporated verbatim into the loop product we are trying to build.

The first thing the macro does is create a lexical environment with
local variables via let, in which a local variable called counter is
bound to the value produced by calling (gensym). In other words, the
value of counter is a unique symbol (symbols are values in LISP). In
this let environment there is but one form that is evaluated; this will
be the return value of the macro, which will be subject to a second
evaluation. This is where we specify the template and substitute things
into it to build up the loop:

	`(dotimes
	   (,counter ,count) ,@forms)))

The backtick means that the entire form is quoted: evaluation
is suppresed, except for things preceded by , and ,@.  These
introduce parameters which are evaluated and interpolated
into the form. The value of counter is a unique symbol, so
,counter causes that unique symbol to be put into the tree.
,count expands to the form that the user specified which
determines the loop count. That form is pinned into our tree.
(See, this is like Christmas!). Lastly, ,@forms means expand
the value of ``forms'' into a list which is spliced into place.
``forms'' evaluates to the unevaluated list of forms.

So now you can write something like

	(ntimes 3 (format t "Hello, ") (format t " world~%"))

to print a message three times. This expands into

	(dotimes (<UNIQUE-SYM> 3) (format t "Hello, ") (format t " world~%"))

(dotimes is itself probably a macro, so more expansion takes place,
but we aren't interested in that).

Now perhaps making a simpler loop based on an existing construct is not
exactly ``undoable'' in other languages, even using primitive C token
preprocessing. But note the differences. This is completely seamless,
and does things at run time, like compute a unique name for a variable.
The only way the user can know that ntimes is a macro is to peek
at the definition or to perform:

	(macro-expand '(ntimes 3 'blah))

to intercept the product. In other words, macro processing in LISP is not
a hacked together text processing kludge like in some other languages,
but a bona fide tree transformation mechanism.
From: ·······@my-deja.com
Subject: Re: when are macros useful?
Date: 
Message-ID: <nm9lmluxqlt.fsf@kindness.mit.edu>
···@ashi.footprints.net (Kaz Kylheku) writes:

> Macros are procedures which, at run time, construct trees
> which are then evaluated.

Run time rather than compile time?  I'd been meaning to reply on this
thread with examples from Scheme macros, but if CL macros are expanded
and evaluated at run time, they're very different beasts, even beyond
the whole "hygienic" issue.

-- 
Bruce R. Lewis				http://brl.sourceforge.net/
I rarely read mail sent to ·······@my-deja.com
From: Coby Beck
Subject: Re: when are macros useful?
Date: 
Message-ID: <Zui37.158303$_T2.31804081@typhoon.tampabay.rr.com>
"Kaz Kylheku" <···@ashi.footprints.net> wrote in message
······························@news1.rdc1.bc.home.com...
> In article <·················@worldnet.att.net>, Ted Sandler wrote:
> >Hi, I've read that lisp macros enable one to do things "undoable" in
> >most other languages.  I was wondering what some examples of these
> >undoable things are, and how they would be implemented using macros.
>
>
> Macros are procedures which, at run time, construct trees
> which are then evaluated.
>

[snip]

> Macros receive their parameters unevaluated, and compute a form,
> usually by carrying out some computation and then substituting
> parameters into a template. The constructed form is returned and
> then evaluated. Because this is done at run time,

My understanding is that macros are expanded at compile time and the above
statements are only true in the case that you are calling compile at
runtime.  While this has great uses it is not the most common way macros are
used (like the defclass example for instance, that all happens at compile
time)

Coby


--
(remove #\space "coby . beck @ opentechgroup . com")
From: Duane Rettig
Subject: Re: when are macros useful?
Date: 
Message-ID: <4itgymaqa.fsf@beta.franz.com>
"Coby Beck" <·····@mercury.bc.ca> writes:

> "Kaz Kylheku" <···@ashi.footprints.net> wrote in message
> ······························@news1.rdc1.bc.home.com...
> 
> > Macros receive their parameters unevaluated, and compute a form,
> > usually by carrying out some computation and then substituting
> > parameters into a template. The constructed form is returned and
> > then evaluated. Because this is done at run time,
> 
> My understanding is that macros are expanded at compile time and the above
> statements are only true in the case that you are calling compile at
> runtime.  While this has great uses it is not the most common way macros are
> used (like the defclass example for instance, that all happens at compile
> time)

No, macros are expanded at macroexpand time.

Cheeky?  Yes, but there's no smiley there because in all seriousness
there is a separate macroexpand time.  It occurs as a sub-time of either
compile time _or_ eval time.

-- 
Duane Rettig          Franz Inc.            http://www.franz.com/ (www)
1995 University Ave Suite 275  Berkeley, CA 94704
Phone: (510) 548-3600; FAX: (510) 548-8253   ·····@Franz.COM (internet)
From: Barry Margolin
Subject: Re: when are macros useful?
Date: 
Message-ID: <W5l37.35$Zv6.327@burlma1-snr2>
In article <·············@beta.franz.com>,
Duane Rettig  <·····@franz.com> wrote:
>"Coby Beck" <·····@mercury.bc.ca> writes:
>
>> "Kaz Kylheku" <···@ashi.footprints.net> wrote in message
>> ······························@news1.rdc1.bc.home.com...
>> 
>> > Macros receive their parameters unevaluated, and compute a form,
>> > usually by carrying out some computation and then substituting
>> > parameters into a template. The constructed form is returned and
>> > then evaluated. Because this is done at run time,
>> 
>> My understanding is that macros are expanded at compile time and the above
>> statements are only true in the case that you are calling compile at
>> runtime.  While this has great uses it is not the most common way macros are
>> used (like the defclass example for instance, that all happens at compile
>> time)
>
>No, macros are expanded at macroexpand time.
>
>Cheeky?  Yes, but there's no smiley there because in all seriousness
>there is a separate macroexpand time.  It occurs as a sub-time of either
>compile time _or_ eval time.

The important thing is that *if* a function is compiled, all the macros
within the function definition will have been expanded at compile time.

In some implementations (e.g. CMUCL), there isn't even an interpreter.
EVAL works by compiling the form and then calling the resulting function.
So, while a traditional interpreter will generally delay expanding a macro
until it's is encountered during execution (which allows them to access
dynamic bindings in the containing form), these compiler-only
implementations will expand all the macros in an expression before
executing anything.

You can see the difference with:

(eval-when (:compile-toplevel :eval)
  (defvar *foo* 1))

(defmacro bad-macro ()
  `(quote ,*foo*))

(eval '(let ((*foo* 2))
         (bad-macro)))

With a typical interpreter, this will return 2, but in a compiler-only
system it will return 1.

-- 
Barry Margolin, ······@genuity.net
Genuity, Burlington, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.
From: Kent M Pitman
Subject: Re: when are macros useful?
Date: 
Message-ID: <sfwd776j9sj.fsf@world.std.com>
Barry Margolin <······@genuity.net> writes:

> (eval-when (:compile-toplevel :eval)...)

:execute, not :eval.

The old-named keywords are eval, compile, load.

The modern names are, respectively -- :execute, :compile-toplevel,
:load-toplevel
From: Pierre R. Mai
Subject: Re: when are macros useful?
Date: 
Message-ID: <871ynm2ehg.fsf@orion.bln.pmsf.de>
Barry Margolin <······@genuity.net> writes:

> The important thing is that *if* a function is compiled, all the macros
> within the function definition will have been expanded at compile time.
> 
> In some implementations (e.g. CMUCL), there isn't even an interpreter.

Minor nitpicking ahead...

Actually, CMU CL does have 2 interpreters, one very simplistic one
that can only handle very trivial forms like function application,
progn, setq, etc., but doesn't depend on (part of) the compiler, and
another interpreter that works directly on the internal representation
of the first pass of the compiler (called IR1).  This can handle all legal
forms, but requires (the first pass) of the compiler to be present.
The simplistic interpreter hands off complex forms to the normal
interpreter.

The simplistic interpreter is important for runtime-only images which
leave out the compiler, but need a way to get the compiled code
running.

So while CMU CL doesn't do a full compile for interpreted code, it
does a full IR1 conversion for complex forms first, and this obviously
includes the macro-expansion phase, thereby making interpreter and
compiler equivalent in this regard.

MCL is an example of a system that AFAIK really is a compiler-only
system.

> You can see the difference with:
> 
> (eval-when (:compile-toplevel :eval)
>   (defvar *foo* 1))
> 
> (defmacro bad-macro () 
>   `(quote ,*foo*))
> 
> (eval '(let ((*foo* 2))
>          (bad-macro)))
> 
> With a typical interpreter, this will return 2, but in a compiler-only
> system it will return 1.

In CMU CL, it will return 1, too, as explained above...

Regs, Pierre.

-- 
Pierre R. Mai <····@acm.org>                    http://www.pmsf.de/pmai/
 The most likely way for the world to be destroyed, most experts agree,
 is by accident. That's where we come in; we're computer professionals.
 We cause accidents.                           -- Nathaniel Borenstein
From: Thomas A. Russ
Subject: Re: when are macros useful?
Date: 
Message-ID: <ymiwv5eufbn.fsf@sevak.isi.edu>
"Pierre R. Mai" <····@acm.org> writes:

> MCL is an example of a system that AFAIK really is a compiler-only
> system.

Actually, MCL also has a very simplistic interpreter for doing things
like SETQ and function calls.  But anything more complicated gets handed
off to the compiler.

-- 
Thomas A. Russ,  USC/Information Sciences Institute          ···@isi.edu    
From: ·······@inetmi.com
Subject: Re: when are macros useful?
Date: 
Message-ID: <ug0c29bi5.fsf@chicago.inetmi.com>
"Pierre R. Mai" <····@acm.org> writes:

> MCL is an example of a system that AFAIK really is a compiler-only
> system.

MCL has an interpreter.  It's just that with the default value of
*compile-definitions* its not used much (if at all).


John Wiseman
From: ·······@my-deja.com
Subject: Re: when are macros useful?
Date: 
Message-ID: <nm9bsmqxdr8.fsf@kindness.mit.edu>
Ted Sandler <··········@worldnet.att.net> writes in comp.lang.lisp:

> Hi, I've read that lisp macros enable one to do things "undoable" in
> most other languages.  I was wondering what some examples of these
> undoable things are, and how they would be implemented using macros.

I'm going to give you an actual example, but first some background:

In terms of a program's potential behavior, all programming languages
are provably equivalent.  It may require extensive effort, but you can
do OOP with inheritance in BASIC.  In that sense there's no "undoable"
thing in any language.

However, in terms of a programming language's ability to extend itself,
there are obvious differences.  For example, the BASIC I used when I
first learned some programming had a fixed set of math operations.
I couldn't add my own.  I could make a program *behave* as if it had
other operations, but calling a BASIC subroutine required more code than
just doing, say, SIN(X).

When I moved on to Forth, I was delighted that now I could write my own
functions, and they were then as convenient to use as the ones built
into the language.  From a theoretical standpoint, this didn't enable
anything previously "undoable".  For me, however, it did.

Previously, I had read a magazine article that described a BASIC program
to make mazes.  I read and reread the article, but somehow couldn't get
the algorithm into my head.  It was too complicated.

After learning some Forth, I was able to go back and understand the
algorithm, because now my mind had a more convenient language to express
it in.  I wrote my own Forth program to generate mazes and was very
proud of myself.

Lisp macros make Lisp different from other languages in a way analagous
to how Forth is different from BASIC.  They allow the user to extend the
language in ways previously undoable, giving convenient expression to
concepts that the language designers might not have anticipated.

I'll give you an example.  Suppose you're looping through an
alphabetized table of words and their definitions.  There may be more
than one definition per word, but you want these grouped by word.

BRL (my Scheme-based web system) has macros useful for such situations.
You use the sql-repeat macro or brl-repeat macro to iterate through the
results.  Within that, you can use (group-beginning? word) to test if
you're at the beginning of a group.

You see, functions can only tell you something about the value of a
variable.  A function would be useless here, because it would only be
passed the current value of word, not its previous value.  (Side note:
group-beginning? can take any expression as its argument, not just a
variable.)

In languages without macros, you could work around this problem by
defining another variable, e.g. priorword, that you set to the value of
word at the end of each iteration, then compare word and priorword.  For
simple problems, this is not a big deal.  However, as things get more
complex, the additional code obscures the core problem being solved.
Even more code is needed for the equivalent of group-ending?.

Not long ago, a coworker was struggling with creating a complex HTML
report from a database using ASP/VBscript.  He had been trying for days,
but it still wasn't working.  He showed me what he was doing, but urged
me not to sink too much time into it.

It was a good deal trickier than your usual HTML report.  It took me 3
or 4 tries to get a BRL solution working.  "Another victory for BRL," my
coworker declared as I gave him the code.

What really surprised me was that, this experienced programmer was not
able to expand the BRL program I gave him into functionally equivalent
VBscript.  Probably some little bug kept him tripped up, and the excess
code made it difficult to track down.  Given enough time, I'm sure he
would have been able to get it.  For practical purposes, i.e. time
constraints, the macro enabled something "undoable" in another language.

-- 
Bruce R. Lewis				http://brl.sourceforge.net/
I rarely read mail sent to ·······@my-deja.com
From: Kaz Kylheku
Subject: Re: when are macros useful?
Date: 
Message-ID: <xyl37.684377$166.14099243@news1.rdc1.bc.home.com>
In article <···············@kindness.mit.edu>, ·······@my-deja.com wrote:
>Ted Sandler <··········@worldnet.att.net> writes in comp.lang.lisp:
>
>> Hi, I've read that lisp macros enable one to do things "undoable" in
>> most other languages.  I was wondering what some examples of these
>> undoable things are, and how they would be implemented using macros.
>
>I'm going to give you an actual example, but first some background:
>
>In terms of a program's potential behavior, all programming languages
>are provably equivalent.  It may require extensive effort, but you can
>do OOP with inheritance in BASIC.  In that sense there's no "undoable"
>thing in any language.

But note that doing OOP with inheritance in BASIC may require the use of
BASIC to write an interpreter for an OOP language, and then writing the
OOP program in that language. One can then no longer say that BASIC made
OOP programming possible, because that same OOP language can be implemented
without using BASIC.

LISP macros allow you to code such interpreters without making a sharp
systemic division between LISP and the new language. Functions and
data pass between LISP and the newly constructed language freely and
seamlessly; for instance one does not give up LISP programming when
using the Common LISP Object System. The system is built up in LISP
and then mixes right in. Basic data types become members of classes,
objects can be put into lists, functions can now be generic, and so on.

But one would likely have to abandon BASIC in using the OOP language
written in BASIC, or at the very best have some awkward glue to translate
between the two, always making it painfully clear which level one is
working in.
From: Larry Loen
Subject: Re: when are macros useful?
Date: 
Message-ID: <3B56585C.D28AB378@us.ibm.com>
Kaz Kylheku wrote:
[snip]
> But note that doing OOP with inheritance in BASIC may require the use of
> BASIC to write an interpreter for an OOP language, and then writing the
> OOP program in that language. One can then no longer say that BASIC made
> OOP programming possible, because that same OOP language can be implemented
> without using BASIC.
> 

Actually, the original author's example was well-chosen.

Most BASICs have:

ON  I GOSUB 100, 200, 300

which could be used to implement the polymorphic call, albeit with much work and obscurity.


Larry
From: Christopher Stacy
Subject: Re: when are macros useful?
Date: 
Message-ID: <uk81549rk.fsf@spacy.Boston.MA.US>
>>>>> On Wed, 18 Jul 2001 21:47:40 -0600, Larry Loen ("Larry") writes:

 Larry> Kaz Kylheku wrote:
 Larry> [snip]
 >> But note that doing OOP with inheritance in BASIC may require the use of
 >> BASIC to write an interpreter for an OOP language, and then writing the
 >> OOP program in that language. One can then no longer say that BASIC made
 >> OOP programming possible, because that same OOP language can be implemented
 >> without using BASIC.

 Larry> Actually, the original author's example was well-chosen.
 Larry> Most BASICs have:
 Larry> ON  I GOSUB 100, 200, 300
 Larry> which could be used to implement the polymorphic call, albeit with much work and obscurity.

Object-oriented programming is a methodology more than a specific
technology, and you can obviously apply it in most languages.
For example, Assembly, BASIC, C, or Lisp.  The question is how 
much the language is going to help or hinder you in doing this.

Since you can construct an abstract data structure somehow (this might
be implemented as a C struct, or set of FORTRAN arrays, or whatever),
you can certainly write your routines to hack the bookeeping for object
identity, type codes, dispatch protocols, etc.  I would not call this
a new language unless the "host" language provided syntactic support
(eg. macros) used to implement it.  There's nothing about this that
implies that you're implementing a full interpreter -  you're just
following a discipline.
From: Barry Margolin
Subject: Re: when are macros useful?
Date: 
Message-ID: <71F57.24$Sp3.439@burlma1-snr2>
In article <·············@spacy.Boston.MA.US>,
Christopher Stacy  <······@spacy.Boston.MA.US> wrote:
>>>>>> On Wed, 18 Jul 2001 21:47:40 -0600, Larry Loen ("Larry") writes:
>
> Larry> Kaz Kylheku wrote:
> Larry> [snip]
> >> But note that doing OOP with inheritance in BASIC may require the use of
> >> BASIC to write an interpreter for an OOP language, and then writing the
> >> OOP program in that language. One can then no longer say that BASIC made
> >> OOP programming possible, because that same OOP language can be implemented
> >> without using BASIC.
>
> Larry> Actually, the original author's example was well-chosen.
> Larry> Most BASICs have:
> Larry> ON  I GOSUB 100, 200, 300
> Larry> which could be used to implement the polymorphic call, albeit
>with much work and obscurity.
>
>Object-oriented programming is a methodology more than a specific
>technology, and you can obviously apply it in most languages.
>For example, Assembly, BASIC, C, or Lisp.  The question is how 
>much the language is going to help or hinder you in doing this.

IMHO, it's not really OO unless the language is doing some kind of
automatic type dispatching, preferably based on runtime, manifest types (as
CLOS and Smalltalk alway do, and C++ does for virtual member functions)
rather than static type declarations, although the latter (as in Ada
generics or C++ overloading and non-virtual members) is better than
nothing.  If the programmer has to code the dispatching himself, using a
BASIC "ON" statement or a C "switch", it doesn't deserve to be called OO;
in that case, it's just the same technique that programmers have been using
since the 50's.  Since OOP was developed to relieve the programmer from
having to do this, it doesn't make sense to use the term to refer to the
very thing that it replaced.  Also, one of the features of OOP is that you
can add new types and not have to go back and update all those places where
you do type dispatching -- the system should automatically incorporate the
new type into its dispatch mechanism.

It's possible to have OOP without explicit language support, by hiding the
dispatching in a library, although some languages may make it difficult, if
not impossible, to accomplish this.  It requires some kind of first-class
data type that can hold the target of a transfer of control, such as
function objects or function pointers.  Without this, I think you would
need a preprocessor to generate the appropriate computed goto, or an
interpreter for the application program, and I wouldn't consider the
language being preprocessed or interpreted to be the same as the target
language (e.g. early versions of C++ were implemented using cfront, a
C++->C translator -- the application was still programmed in C++, not C).

-- 
Barry Margolin, ······@genuity.net
Genuity, Burlington, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.
From: Kaz Kylheku
Subject: Re: when are macros useful?
Date: 
Message-ID: <EtH57.12814$h8.178506@news1.rdc1.bc.home.com>
In article <·················@us.ibm.com>, Larry Loen wrote:
>
>
>Kaz Kylheku wrote:
>[snip]
>> But note that doing OOP with inheritance in BASIC may require the use of
>> BASIC to write an interpreter for an OOP language, and then writing the
>> OOP program in that language. One can then no longer say that BASIC made
>> OOP programming possible, because that same OOP language can be implemented
>> without using BASIC.
>> 
>
>Actually, the original author's example was well-chosen.
>
>Most BASICs have:
>
>ON  I GOSUB 100, 200, 300
>
>which could be used to implement the polymorphic call, albeit with much work and obscurity.

Once you start making the program data-driven like this, you are on your
way toward writing an interpreter for the language you really want to be
programming in.

All you then need is some parser to take some more convenient notation
to generate that driving data, instead of sweating it out yourself.

E.g. I can code up a regular expression matching machine by declaring some
arrays in language X, and stuffing them with magic numbers representing
state transitions, and then call a state machine function which operates
on those arrays.  Am I then still programming in X, or am I programming in
the language of those numbers?  Some skeptics might say that I'm still
programming in X, because I'm punching those numbers into a X array
after all, using the X syntax, and then calling the matcher automaton
using an X function call.  Then I can take that one step further and
write a routine that parses a string like "a.(b|c)*" and writes those
tables for me.  When I specify those strings, am I still writing in
X? Clearly not.

But what if X had the built-in capability to parse its own syntax, and
allow the programmer to add new meanings and constructs to it, and 
seamlessly mix it all into an X program? Then I could write regular
expressions, but they would look like X expressions, and I could still
claim that I'm working in X. I would not have to explicitly invoke some
compiler routine for these expressions to run them, and would not
have to convert their result back to the ``native'' type system.

If X is BASIC, then the answer is no: you cannot do any such thing.
The more data-driven you make the program, the more it resembles
an interpreter, and the greater the systemic division between the
implementing language and the implemented language.