From: Jacek Generowicz
Subject: List comprehension
Date: 
Message-ID: <g0g07swawu.fsf@scumbag.ecs.soton.ac.uk>
I haven't had the time to play with lisp for a couple of months :-(

Finally I found a few spare moments and tried to knock together a
list-comprehension macro, which I submit below in order to solicit
constructive criticism.

(defmacro select (expression &rest symbol-sequence-pairs)
  (labels ((nest-loops (ss-pairs)
		(let ((symbol (caar ss-pairs))
		      (list (reverse (cdar ss-pairs))))
		  (case (length ss-pairs)
		    (0 '(list))
		    (1
		     `(dolist (,symbol ,@list result)
			(let ((element ,expression))
			  (when element
			    (setq result (cons element result))))))
		    (otherwise
		     `(dolist (,symbol ,@list result)
			,(nest-loops (cdr ss-pairs))))))))
	   `(let ((result ()))
	     (reverse ,(nest-loops symbol-sequence-pairs)))))


Sample usage:

(select a (a '(1 2 3)))  =>  (1 2 3)
		  
(select (when (>= a b) (list a b))
	(a '(1 2 3))
	(b '(1 2 3)))

=> ((1 1) (2 1) (2 2) (3 1) (3 2) (3 3))


Jacek

From: Kent M Pitman
Subject: Re: List comprehension
Date: 
Message-ID: <sfwvggoc9ak.fsf@world.std.com>
Jacek Generowicz <···@ecs.soton.ac.uk> writes:

> I haven't had the time to play with lisp for a couple of months :-(
> 
> Finally I found a few spare moments and tried to knock together a
> list-comprehension macro, which I submit below in order to solicit
> constructive criticism.
> 
> (defmacro select (expression &rest symbol-sequence-pairs) ...)

Well, I'd definitely call it something other than SELECT, which to me 
suggests that it takes an already-enumerated data set and finds a piece
or pieces of it.  In your case, you're first GENERATING tuples and only 
then selecting.

Btw, the uses of ,@list trouble me in your expansion because you are
splicing into dolist's binding list, which expects a very specific
number of items.  In the (1) clause, this is almost ok, but I'd prefer
,(first x).  [I often make and use a function ONLY which is like FIRST
but signals an error if the list has other than one element.]
I'm even less sure of the ,@list in the OTHERWISE clause [which to my
sleepy eyes doesn't look like it would ever work, since it must introduce
at least 2 elements, and the dolist binding list takes a max of 3 elements,
... but I didn't try it] and would rather see you be explicit.

Others will doubtless comment on other details, probably pointing  you
at Dick Waters iteration stuff in CLTL2, etc., so I won't bother. ;-)
From: Jacek Generowicz
Subject: Re: List comprehension
Date: 
Message-ID: <g08zdhwtjf.fsf@scumbag.ecs.soton.ac.uk>
Kent M Pitman <······@world.std.com> writes:
> Others will doubtless comment on other details, probably pointing you
> at Dick Waters iteration stuff in CLTL2,

Thanks for this pointer, looks interesting. 

Has there been any progress towards deciding whehter series or
generators are going to be included in the standard ?
From: Kent M Pitman
Subject: Re: List comprehension
Date: 
Message-ID: <sfwpu6tid10.fsf@world.std.com>
Jacek Generowicz <···@ecs.soton.ac.uk> writes:

> Kent M Pitman <······@world.std.com> writes:
> > Others will doubtless comment on other details, probably pointing you
> > at Dick Waters iteration stuff in CLTL2,
> 
> Thanks for this pointer, looks interesting. 
> 
> Has there been any progress towards deciding whehter series or
> generators are going to be included in the standard ?

The operational answer to this question is "no" but that gives the
wrong intuitional sense because your use of the word "progress"
implies a default understanding that such activities are or should be
in progress and that the absence of such progress would be correctly
described by "non-progress", "deadlock", , or some other word with
attached connotations that probably misleadingly implies laziness,
stubbornness, lack of initiative, confusion about what's valuable, or
some other unduly negative view.

Effectively, this is not the right question.

The right question is whether there is substantial enough reason to modify
the standard at all.

One can't re-open the standards process on condition that only certain
changes be made.  It can only be reopened generally, and that can
destabilize other things.  I resigned from the committee, but at last
I checked, its posture was that things weren't enough of a problem that it
was worth the risk and expense.

The expense is that even a tiny change means re-tooling all deployed
applications, or checking them for compatibility.  This is a large expense
probably in the millions of dollars community-wide.  The general belief is
that such money, if it is available community-wide, is better spent producing
value with what's there than fussing over small details of what could be,
ESPECIALLY when such things can be added as add-ons, which so far seems to
largely be the case.

The best possible spin on the current situation is that ANSI CL has been
seen to be strong enough that the community is properly empowered to work
out desires such as you express at a marketplace-level, which in principle
means you get access to such things in market-time (as close to realtime
as is commercially practical) rather than standards-time (as close to 
glacial as can be emulated using people in a conference room instead of
frozen h2o on a large plain).  So the lack of progress, such as you 
effectively describe it, should be seen as a positive thing.  Or so say
I with my "spin doctor" hat on.

(Disclaimer: These are all my personal perceptions, and even where I'm
observing the state of the committee do not represent the official
position of the committee.)
From: Jacek Generowicz
Subject: Re: List comprehension
Date: 
Message-ID: <g01yj9wc69.fsf@scumbag.ecs.soton.ac.uk>
Kent M Pitman <······@world.std.com> writes:

> Jacek Generowicz <···@ecs.soton.ac.uk> writes:
> 
> > Kent M Pitman <······@world.std.com> writes:
> > > Others will doubtless comment on other details, probably pointing you
> > > at Dick Waters iteration stuff in CLTL2,
> > 
> > Thanks for this pointer, looks interesting. 
> > 
> > Has there been any progress towards deciding whehter series or
> > generators are going to be included in the standard ?


> The operational answer to this question is "no" but that gives the
> wrong intuitional sense because your use of the word "progress"
> implies a default understanding that such activities are or should be
> in progress and that the absence of such progress would be correctly
> described by "non-progress", "deadlock", , or some other word with
> attached connotations that probably misleadingly implies laziness,
> stubbornness, lack of initiative, confusion about what's valuable, or
> some other unduly negative view.
> 
> Effectively, this is not the right question.
> 
> The right question is whether there is substantial enough reason to modify
> the standard at all.

[In depth discussion of merits and interpetations of progress
deleted.]

Whoa! I wasn't implying that I want anything added to the
standard. The preface of CLtL2 states (unless I was hallucnating -
quite possible as I was reading it late at night after a hard day)
that although X3J13 had considered including the material in the
standard and voted against it, it would be considered in the future
when it has matured. Given that those remarks were written over a
decade ago, I was merely wondering how the situation had evolved in
the interim.
From: Kent M Pitman
Subject: Re: List comprehension
Date: 
Message-ID: <sfwsnbp6yze.fsf@shell01.TheWorld.com>
Jacek Generowicz <···@ecs.soton.ac.uk> writes:

> Kent M Pitman <······@world.std.com> writes:
>
> > The right question is whether there is substantial enough reason to modify
> > the standard at all.
> 
> [In depth discussion of merits and interpetations of progress
> deleted.]
> 
> Whoa! I wasn't implying that I want anything added to the
> standard. The preface of CLtL2 states (unless I was hallucnating -
> quite possible as I was reading it late at night after a hard day)
> that although X3J13 had considered including the material in the
> standard and voted against it, it would be considered in the future
> when it has matured. Given that those remarks were written over a
> decade ago, I was merely wondering how the situation had evolved in
> the interim.

Once again an explanation is in order.

First, on the issue of reliability:

CLTL2 was Steele's product, not the X3J13 committee's.

This is confusing because CLTL(1) was a committee product, only
incidentally authored by Steele, just as ANSI CL was a committee
product, only incidentally edited by myself.

The content of CLTL was therefore reviewed and approved by a committee.
The content of CLTL was NOT reviewed by nor was it approved by a committee.

It's possible that the remarks about future consideration were
Steele's optimism, not the committee's.  I personally felt it was
inappropriate for him to make the book at all (because of the
confusion I just cited), and especially inappropriate for him to add
text to it that was not committee product (which compounded the confusion).

I wrestled with this in CLHS, for example, and made the decision not to
include failed cleanup issues (which were probably even more numerous
than successful ones).  I think the failed issues might merit some display
somewhere, but I was worried about the perception if they rode along with
other stuff that wasn't approved.  [I also still have some lingering regret
that I didn't mark the sections of the issues I did publish that were the
failed portions (when a document contained both succeeding and failing
parts) more clearly.]

Second, on the issue of future desire:

Sometimes when people spend a lot of time on a major proposal and a
committee doesn't accept it, it adds words like "we might consider
this in the future" more as a way to soften the blow or apologize for
the effort spent than to say "oh, yes, this is definitely what we
want".

I'm not saying that such a thing happened here.  But one must always 
keep in mind that it's possible and never read statements made in that
particular case as necessarily "face value", unless you mean ("saving face"
"value").

Just to go out on a limb to make the point here, I would personally
vote against adding Waters' iteration facility to the language if it
were added today, not because I don't think it's conceptually cool
(which I do), and not because I think it stylistically clashes (which
it may), and not because the language is big enough already and this
could be added as a library (which is probably so), but because I've
only ever seen one implementation and I'm nervous about it.  It uses
COMPILER-LET for much of its dataflow, and that ill-defined primitive
was thankfully removed from the language.  The various people who've
tried to rewrite it have failed, as far as I know.  (I worked on it
for about a solid week and eventually timed out, worried that it would
take much longer.)  If there were a portable, reliable
whole-expression macroexpander, it might be possible to work around
the COMPILER-LET problem, but so far as I know no one has one of those
either.  I would want to see at least one clean implementation, and
preferrably an independent one, just to assure myself that the details
were solid.  So whether it would or would not qualify as something
"tested" is hard to say, and that might influence a vote.  But the
reason there has not been a vote has nothing to do with any of this;
this is just what would happen if it did fall through to a vote, and
why you should read words like "we might consider it in the future"
with the suspicion as you read words like "I'm fine" when you ask 
someone "how are you doing today?" in the hall as you pass quickly by.

ALSO, we were encouraged as we got closer to publication that it was a bad
idea to write words like "in the future, x will happen".  This was an
issue surrounding deprecation, for example.  There's no way to predict
what the people in the future will do, and it's a bad idea to tie their
hands by making false promises.  For example, although most users have
accepted with gratitude the deprecation of :TEST-NOT keywords, they have
reacted quite negatively to the deprecation of -IF-NOT operators, and I'm
quite sure that if the issue were reopened for vote, the latter would be
"rescued".
From: Erik Naggum
Subject: Re: List comprehension
Date: 
Message-ID: <3214297883851965@naggum.net>
* Kent M Pitman <······@world.std.com>
| One can't re-open the standards process on condition that only certain
| changes be made.  It can only be reopened generally, and that can
| destabilize other things.

  Considering that we have a few people (still) in the community who want
  to redefine if and remove when, unless, and probably extended loop, too,
  who fight against upper-case symbol names, want to get rid of characters
  and leave us with the C-like stupidity of conflating characters and small
  integers, etc, and who will _not_ quit regardless of how much negative
  feedback they receive for their "efforts", it is impossible to reopen the
  standard for danger of having someone like that fight tooth and nail for
  their favorite personal issues to the detriment of us all.  Again, it is
  not that they want something for themselves, it is that they are so
  arrogant and egoistic that they want everybody else not to have something
  they do not want.  That kind of attitude is incompatible with responsible
  participation in the standards process, and is frankly so dangerous that
  those who want the standard not only to be stable, but to remain a
  standard at all, need to keep it from reopening until the rebels can be
  controlled.  This means that one of the reasons that we cannot add stuff
  to the standard that people ask for and for which there is sufficient
  consensus is that there is at least one prominent nutcase out there who
  does not want to be part of the consensus-creation process and who cannot
  be trusted not to shy away from derailing the whole standard if he does
  not get his pet peeves "serviced" by the committee.  The strong hostility
  towards the choice of upper-case symbol names alone should make everyone
  nervous about reopening the standard.

  In the SGML community, stability of the standard was considered much more
  important than we see the stability of ours, and the terrible fighting
  that took place in order to keep representatives of several countries who
  did not quite grasp the purpose of a standard and who wanted to change a
  lot of small things that would have meant the rest of the world would
  have been put on hold waiting for the last such nutcase to be satisfied.
  The danger in an open committee is that it cannot exclude people who are
  too narrow-minded, selfish, or stupid to figure out how their "desires"
  will impact other people if implemented, or who even do not care.  As
  long as such people are vocal in the community, _nothing_ will happen in
  the form of standards and other political arenas.  If such people are not
  excised from the community, _all_ political processes will halt pending
  the emergence of responsibility and stability and respect for the process
  and the results that it produces.  The SGML community tried to shut up
  its nutcases by writing a document that detailed what kinds of changes
  would be allowed, and had all the countries vote on this document, which
  at the time looked draconian and dictatorial, but it turned out to be
  quite necessary to keep a few vocal nutcases from going postal and cut
  and slash at random from the standard, primarily motivated by a sense of
  "competition" from ODA.  This worked for a few years, until W3C took over
  the lead to create XML, which has certainly destabilized the SGML world
  and wasted enormous amounts of money on re-tooling applications, despite
  all the political verbiage to the contrary.

  I am deeply concerned about the future of Common Lisp because of the lack
  of respect for the one document of community agreement that we have today.
  That some renegade nutcase is so irrationally opposed to upper-case and
  to the standard conditionals means that we will spend lots and lots of
  energy keeping the renegade nutcase from damaging what we have managed to
  scrape together and hold together with chewing gum and duct tape _because_
  of the nutcase's incessant rantings about the flaws of the standard.  If
  we could get rid of the overly vocal nutcase, or he could figure out that
  he has _quit_ the community and should just go away and make his own Lisp,
  the stability of the standard would improve so much that people could
  again look to it and _trust_ that stability and use the document itself
  for reference and _expect_ it to be implemented fully and correctly, as
  opposed to having to wonder what influence the nutcase has had on the
  willingness to implement the many parts of the standard he rebels against.
  Once we have that sense of stability and trust, we can work to improve it.

///
-- 
  Norway is now run by a priest from the fundamentalist Christian People's
  Party, the fifth largest party representing one eighth of the electorate.
-- 
  Carrying a Swiss Army pocket knife in Oslo, Norway, is a criminal offense.
From: Chris Riesbeck
Subject: Re: List comprehension
Date: 
Message-ID: <riesbeck-D78D90.14451306112001@news.it.nwu.edu>
In article <··············@scumbag.ecs.soton.ac.uk>, Jacek Generowicz 
<···@ecs.soton.ac.uk> wrote:

>Sample usage:
>
>(select a (a '(1 2 3)))  =>  (1 2 3)
>       
>(select (when (>= a b) (list a b))
>  (a '(1 2 3))
>  (b '(1 2 3)))
>
>=> ((1 1) (2 1) (2 2) (3 1) (3 2) (3 3))

Like Kent, I think SELECT is misleading. My own version is called
LIST-OF, as in  "list of A, where A is in (1 2 3)."

Second, there's a more flexible syntax that's still easy
to implement:

  (list-of exp form form form ...)

exp is what you collect, and form is either 
(variable list-value) or (WHEN test).  

Using left-to-right evaluation and scoping on forms, 
your two examples become

  (list-of a (a '(1 2 3))

and

  (list-of (list a b) (a '(1 2 3)) (b '(1 2 3)) (when (>= a b))

With this syntax, you can filter values more efficiently, e.g., 

  (list-of (list a b) (a ...) (when (> a 100)) (b ...) (when (> b a)))

If there are N A's and M B's, but only 1 A > 100, this does 
only M calls to (> B A), not N x M.

Also, your implementation returned NIL with no forms, but I think

  (list-of 1)

should return (1).

>(defmacro select (expression &rest symbol-sequence-pairs)
>  (labels ((nest-loops (ss-pairs)
>		(let ((symbol (caar ss-pairs))
>		      (list (reverse (cdar ss-pairs))))
>		  (case (length ss-pairs)
>		    (0 '(list))
>		    (1
>		     `(dolist (,symbol ,@list result)
>			(let ((element ,expression))
>			  (when element
>			    (setq result (cons element result))))))
>		    (otherwise
>		     `(dolist (,symbol ,@list result)
>			,(nest-loops (cdr ss-pairs))))))))
>	   `(let ((result ()))
>	     (reverse ,(nest-loops symbol-sequence-pairs)))))

My pattern for defining macros is to call an expander
function for any non-trivial expansion, e.g., 

  (defmacro list-of (exp &rest args)
    (expand-list-of exp args))

This lets me easily trace what's going on, and I can do recursion
without needing a LABELS.

I found it a lot simpler to expand (variable list-value)
into a MAPCAN, rather than a DOLIST. If you handle (LIST-OF 1)
in the obvious way, the MAPCAN won't be side-effecting any list
except the one being built internally.
From: Jacek Generowicz
Subject: Re: List comprehension
Date: 
Message-ID: <g0668lwjc9.fsf@scumbag.ecs.soton.ac.uk>
Chris Riesbeck <········@exchange.cs.northwestern.edu> writes:

> Second, there's a more flexible syntax that's still easy
> to implement:
> 
>   (list-of exp form form form ...)
> 
> exp is what you collect, and form is either 
> (variable list-value) or (WHEN test).  
> 
> Using left-to-right evaluation and scoping on forms, 
> your two examples become
> 
>   (list-of a (a '(1 2 3))
> 
> and
> 
>   (list-of (list a b) (a '(1 2 3)) (b '(1 2 3)) (when (>= a b))


> My pattern for defining macros is to call an expander
> function for any non-trivial expansion, e.g., 
> 
>   (defmacro list-of (exp &rest args)
>     (expand-list-of exp args))
> 
> This lets me easily trace what's going on, and I can do recursion
> without needing a LABELS.


> I found it a lot simpler to expand (variable list-value)
> into a MAPCAN, rather than a DOLIST. If you handle (LIST-OF 1)
> in the obvious way, the MAPCAN won't be side-effecting any list
> except the one being built internally.

Something along these lines ?

(defmacro list-of (expr &rest args)
  (expand-list-of expr args))

(defun expand-list-of (expr args)
  (print args)
  (if (zerop (length args))
      `(list ,expr)
    (if (and (cdr args) (eq 'when (caadr args)))
	`(mapcan #'(lambda (,(caar args))
		     ,(append (cadr args) 
			      (list (expand-list-of expr (cddr args)))))
		 ,@(cdar args))
      `(mapcan #'(lambda (,(caar args))
		   ,(expand-list-of expr (cdr args)))
	       ,@(cdar args)))))
From: Chris Riesbeck
Subject: Re: List comprehension
Date: 
Message-ID: <riesbeck-0315D8.12311908112001@news.it.nwu.edu>
In article <··············@scumbag.ecs.soton.ac.uk>, Jacek Generowicz 
<···@ecs.soton.ac.uk> wrote:

>(defun expand-list-of (expr args)
>  (print args)
>  (if (zerop (length args))

Pop quiz: What Lisp primitive will return true or false 
here in constant time, unlike the above which depends on
the length of args?

>      `(list ,expr)
>    (if (and (cdr args) (eq 'when (caadr args)))
>        `(mapcan #'(lambda (,(caar args))
>                     ,(append (cadr args) 
>                              (list (expand-list-of expr (cddr args)))))
>                 ,@(cdar args))
>      `(mapcan #'(lambda (,(caar args))
>                   ,(expand-list-of expr (cdr args)))
>               ,@(cdar args)))))

Close but it's even simpler and more intuitive. 

(WHEN test) can expand into (WHEN test ...), while (variable list)
expands into (MAPCAN ... list).
From: Jochen Schmidt
Subject: Re: List comprehension
Date: 
Message-ID: <9ser3j$hii$1@rznews2.rrze.uni-erlangen.de>
Chris Riesbeck wrote:

> In article <··············@scumbag.ecs.soton.ac.uk>, Jacek Generowicz
> <···@ecs.soton.ac.uk> wrote:
> 
>>(defun expand-list-of (expr args)
>>  (print args)
>>  (if (zerop (length args))
> 
> Pop quiz: What Lisp primitive will return true or false
> here in constant time, unlike the above which depends on
> the length of args?

(defun expand-list-of (expr args)
  (print args)
  (if (null args)

What can I win if that is right? ;-)


ciao,
Jochen
 
--
http://www.dataheaven.de
From: Tim Bradshaw
Subject: Re: List comprehension
Date: 
Message-ID: <fbc0f5d1.0111090304.693e30ac@posting.google.com>
Chris Riesbeck <········@exchange.cs.northwestern.edu> wrote in message news:<······························@news.it.nwu.edu>...

> 
> Pop quiz: What Lisp primitive will return true or false 
> here in constant time, unlike the above which depends on
> the length of args?

I have a disturbing function called LENGTH-ATLEAST (I think) which has
a compler macro such that calls like (LENGTH-ATLEAST x 2) get
rewritten as (not (null (cdr x))).  If the second argument is not
constant it still works by iterating only as far as it needs to.

(It's not inherently disturbing, it just disturbs me that I wrote it -
it's kind of the thinking-persons version of the standard C
micro-optimisation.)

I don't think it special-cases 0 though, maybe it should.

--tim
From: Kent M Pitman
Subject: Re: List comprehension
Date: 
Message-ID: <sfwofmdicuu.fsf@world.std.com>
Jacek Generowicz <···@ecs.soton.ac.uk> writes:

> Something along these lines ?
> 
> (defmacro list-of (expr &rest args)
>   (expand-list-of expr args))
> 
> (defun expand-list-of (expr args)
>   (print args)

On the Lisp Machine, merely including a call to BREAK in your code used
to cause:

 Warning: Breakpoint in code.

One might think this dumb, but it was tolerable at debug time and meant
it was easy to find stray debugging breakpoints left in code they were
going to deploy.

I often think there should be a DEBUG-PRINT which is like PRINT (though
often alternate syntaxes or arg conventions are better; that's irrelevant
to my point here) but also warns when you compile, so you can find stray
PRINT statements like this left in stuff you're distributing.

Surely you didn't mean EXPAND-LIST-OF to do I/O. ;-)

>   (if (zerop (length args))
>       `(list ,expr)
>     (if (and (cdr args) (eq 'when (caadr args)))
> 	`(mapcan #'(lambda (,(caar args))
> 		     ,(append (cadr args) 
> 			      (list (expand-list-of expr (cddr args)))))
> 		 ,@(cdar args))
>       `(mapcan #'(lambda (,(caar args))
> 		   ,(expand-list-of expr (cdr args)))
> 	       ,@(cdar args)))))
From: Jacek Generowicz
Subject: Re: List comprehension
Date: 
Message-ID: <g0wv11uxfu.fsf@scumbag.ecs.soton.ac.uk>
Kent M Pitman <······@world.std.com> writes:

> Jacek Generowicz <···@ecs.soton.ac.uk> writes:

> > (defun expand-list-of (expr args)
> >   (print args)

> Surely you didn't mean EXPAND-LIST-OF to do I/O. ;-)

Indeed, as soon as I sent the message, I realized I had left in that
debugging aid.
From: Pierre R. Mai
Subject: Re: List comprehension
Date: 
Message-ID: <87hes59ofc.fsf@orion.bln.pmsf.de>
Kent M Pitman <······@world.std.com> writes:

> I often think there should be a DEBUG-PRINT which is like PRINT (though
> often alternate syntaxes or arg conventions are better; that's irrelevant
> to my point here) but also warns when you compile, so you can find stray
> PRINT statements like this left in stuff you're distributing.

In the vein of your pro-stable ANSI standard message on this thread,
ANSI CL is strong enough to enable one to portably define such a
thing on your own:

(defun debug-format (format &rest args)
  (apply #'format *trace-output* format args))

(define-compiler-macro debug-format (&whole form format &rest args)
  (warn "~A form in code:~%  ~S" 'debug-format form)
  `(format *trace-output* ,format ,@args))

With the usual caveat that if the compiler keeps re-expanding
compiler-macros, then you'll get more than one warning per compiled
debug-format form.  One could work around this problem with some
dubious hacks, but most available implementations only expand compiler
macros once during runs of the compiler.  If they expand them at all,
that is, but that's something the market-place can sort out.

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: Kent M Pitman
Subject: Re: List comprehension
Date: 
Message-ID: <sfw7kt1w3no.fsf@shell01.TheWorld.com>
"Pierre R. Mai" <····@acm.org> writes:

> Kent M Pitman <······@world.std.com> writes:
> 
> > I often think there should be a DEBUG-PRINT which is like PRINT (though
> > often alternate syntaxes or arg conventions are better; that's irrelevant
> > to my point here) but also warns when you compile, so you can find stray
> > PRINT statements like this left in stuff you're distributing.
> 
> In the vein of your pro-stable ANSI standard message on this thread,
> ANSI CL is strong enough to enable one to portably define such a
> thing on your own:

Oh, I absolutely agree.  I was going to post this example but was too lazy
(well, ok, too busy reading Slashdot feedback).  Anyway, thanks for saving
me the trouble!

> 
> (defun debug-format (format &rest args)
>   (apply #'format *trace-output* format args))
> 
> (define-compiler-macro debug-format (&whole form format &rest args)
>   (warn "~A form in code:~%  ~S" 'debug-format form)
>   `(format *trace-output* ,format ,@args))

You can also just declare format and args ignored and return form here, 
I think.

> With the usual caveat that if the compiler keeps re-expanding
> compiler-macros, then you'll get more than one warning per compiled
> debug-format form.  One could work around this problem with some
> dubious hacks, but most available implementations only expand compiler
> macros once during runs of the compiler.  If they expand them at all,
> that is, but that's something the market-place can sort out.

Yes, I think that's right.  (I'd also advise the compiler to do the dubious
hack of buffering warnings and avoiding identical ones, rather than each
macro or compiler macro doing it...)
From: Pierre R. Mai
Subject: Re: List comprehension
Date: 
Message-ID: <871yj99cwy.fsf@orion.bln.pmsf.de>
Kent M Pitman <······@world.std.com> writes:

> > (defun debug-format (format &rest args)
> >   (apply #'format *trace-output* format args))
> > 
> > (define-compiler-macro debug-format (&whole form format &rest args)
> >   (warn "~A form in code:~%  ~S" 'debug-format form)
> >   `(format *trace-output* ,format ,@args))
> 
> You can also just declare format and args ignored and return form here, 
> I think.

Yes, just thought that I might do the obvious optimization along the
way...

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