From: Chris Capel
Subject: flets in macros
Date: 
Message-ID: <10s5lgfig2ni08a@corp.supernews.com>
I'm writing a macro, map-collect, that performs a mapping function over a
list or lists and binds COLLECT and NEXT to functions that allow the
following:

(map-collect mapcar (item '(1 2 3 4 5 6))
  (collect item)
  (when (evenp item)
    (next)))

==> ((1 2) (3 4) (5 6) ())

Now, my problem is that I'm defining this an a utility package, but I don't
want to have to put on package qualifiers to the collect and next symbols
every time I use it in other packages. I also really don't want to write a
code-walker in my macro to manually replace any symbol with the right
symbol name. So I figure I have two options. Either I export both COLLECT
and NEXT, or I do something like this:

(defmacro map-collect (...)
  `( ...
     (flet ((,(read-from-string "collect") ...)
            (,(read-from-string "next") ...)
       ...)))

The latter approach works, and I sort of like it. Is either preferable?

Chris Capel

From: Peter Seibel
Subject: Re: flets in macros
Date: 
Message-ID: <m33by4hp4q.fsf@javamonkey.com>
Chris Capel <······@iba.nktech.net> writes:

> I'm writing a macro, map-collect, that performs a mapping function over a
> list or lists and binds COLLECT and NEXT to functions that allow the
> following:
>
> (map-collect mapcar (item '(1 2 3 4 5 6))
>   (collect item)
>   (when (evenp item)
>     (next)))
>
> ==> ((1 2) (3 4) (5 6) ())
>
> Now, my problem is that I'm defining this an a utility package, but I don't
> want to have to put on package qualifiers to the collect and next symbols
> every time I use it in other packages. I also really don't want to write a
> code-walker in my macro to manually replace any symbol with the right
> symbol name. So I figure I have two options. Either I export both COLLECT
> and NEXT, or I do something like this:
>
> (defmacro map-collect (...)
>   `( ...
>      (flet ((,(read-from-string "collect") ...)
>             (,(read-from-string "next") ...)
>        ...)))
>
> The latter approach works, and I sort of like it. Is either preferable?

Exporting COLLECT and NEXT is preferable. COLLECT and NEXT are part of
the public API of MAP-COLLECT so they should be exported just like any
other names that are part of the public API. You should not pollute
other folk's name spaces.

Also, since the READ-FROM-STRING is going to happen at macro-expand
time, which is different from the read time of the code that uses
MAP-COLLECT, there is no guarantee (though it is, of course, likely)
that *PACKAGE* will be set to the same value at both times. The rules
requiring *PACKAGE* to be set the same when you LOAD a file as when
you COMPILE-FILE it probably give you some cover there. But you're
asking for trouble it seems to me. Don't fight the language.

-Peter

-- 
Peter Seibel                                      ·····@javamonkey.com

         Lisp is the red pill. -- John Fraser, comp.lang.lisp
From: Chris Capel
Subject: Re: flets in macros
Date: 
Message-ID: <10s83jjf38vqpf2@corp.supernews.com>
Peter Seibel wrote:

> Chris Capel <······@iba.nktech.net> writes:
> 
>> I'm writing a macro, map-collect, that performs a mapping function over a
>> list or lists and binds COLLECT and NEXT to functions that allow the
>> following:
>>
>> (map-collect mapcar (item '(1 2 3 4 5 6))
>>   (collect item)
>>   (when (evenp item)
>>     (next)))
>>
>> ==> ((1 2) (3 4) (5 6) ())
>>
>> Now, my problem is that I'm defining this an a utility package, but I
>> don't want to have to put on package qualifiers to the collect and next
>> symbols every time I use it in other packages. I also really don't want
>> to write a code-walker in my macro to manually replace any symbol with
>> the right symbol name. So I figure I have two options. Either I export
>> both COLLECT and NEXT, or I do something like this:
>>
>> (defmacro map-collect (...)
>>   `( ...
>>      (flet ((,(read-from-string "collect") ...)
>>             (,(read-from-string "next") ...)
>>        ...)))
>>
>> The latter approach works, and I sort of like it. Is either preferable?
> 
> Exporting COLLECT and NEXT is preferable. COLLECT and NEXT are part of
> the public API of MAP-COLLECT so they should be exported just like any
> other names that are part of the public API. You should not pollute
> other folk's name spaces.

What, you mean like LOOP does?

NEXT and COLLECT and pretty common symbol names, I imagine. And exporting
the things are probably going to cause a lot more "polluting" than
otherwise. What if I have a function in my user package named USER::NEXT?
What happens when I want to use MAP-COLLECT? I have to shadow UTILS:NEXT
and write "(utils:next)" in my macro body. I could make that clearer if I
put MAP-COLLECT in its own package, but then it's more typing to qualify
it.

> Also, since the READ-FROM-STRING is going to happen at macro-expand
> time, which is different from the read time of the code that uses
> MAP-COLLECT, there is no guarantee (though it is, of course, likely)
> that *PACKAGE* will be set to the same value at both times. The rules
> requiring *PACKAGE* to be set the same when you LOAD a file as when
> you COMPILE-FILE it probably give you some cover there. But you're
> asking for trouble it seems to me. Don't fight the language.

I'm not fighting it. I've seen macros in others' code that does the same
thing, using MAKE-SYMBOL and INTERN. I prefer READ-FROM-STRING, as it
combines both steps into one and respects readcase.

Chris Capel
From: Peter Seibel
Subject: Re: flets in macros
Date: 
Message-ID: <m31xdnej2o.fsf@javamonkey.com>
Chris Capel <······@iba.nktech.net> writes:

> Peter Seibel wrote:
>
>> Chris Capel <······@iba.nktech.net> writes:
>> 
>>> I'm writing a macro, map-collect, that performs a mapping function over a
>>> list or lists and binds COLLECT and NEXT to functions that allow the
>>> following:
>>>
>>> (map-collect mapcar (item '(1 2 3 4 5 6))
>>>   (collect item)
>>>   (when (evenp item)
>>>     (next)))
>>>
>>> ==> ((1 2) (3 4) (5 6) ())
>>>
>>> Now, my problem is that I'm defining this an a utility package, but I
>>> don't want to have to put on package qualifiers to the collect and next
>>> symbols every time I use it in other packages. I also really don't want
>>> to write a code-walker in my macro to manually replace any symbol with
>>> the right symbol name. So I figure I have two options. Either I export
>>> both COLLECT and NEXT, or I do something like this:
>>>
>>> (defmacro map-collect (...)
>>>   `( ...
>>>      (flet ((,(read-from-string "collect") ...)
>>>             (,(read-from-string "next") ...)
>>>        ...)))
>>>
>>> The latter approach works, and I sort of like it. Is either preferable?
>> 
>> Exporting COLLECT and NEXT is preferable. COLLECT and NEXT are part of
>> the public API of MAP-COLLECT so they should be exported just like any
>> other names that are part of the public API. You should not pollute
>> other folk's name spaces.
>
> What, you mean like LOOP does?

Well, LOOP doesn't bind the names is uses as "keywords"--it implements
its own grammar (essentially a shallow tree walker). You can use the
same symbol both as a LOOP keyword and for your own purposes:

  (flet ((collect (x) (format t "Collecting ~a~%" x)))
    (loop for i from 1 to 10 collect i do (collect i)))

In other words LOOP is using the symbols purely as syntactic markers.
If you want to use symbols as syntactic markers and then decide to
care only about the SYMBOL-NAME of symbols being used that way, that's
fine because you're not dorking up other folk's existing bindings.

> NEXT and COLLECT and pretty common symbol names, I imagine. And
> exporting the things are probably going to cause a lot more
> "polluting" than otherwise. What if I have a function in my user
> package named USER::NEXT? What happens when I want to use
> MAP-COLLECT? I have to shadow UTILS:NEXT and write "(utils:next)" in
> my macro body. I could make that clearer if I put MAP-COLLECT in its
> own package, but then it's more typing to qualify it.

If you use the package system then the user has the option of not
USE-PACKAGE'ing your package and refering to *your* NEXT and COLLECT
with explicitly package-qualified names. At any rate at least they can
still refer to both NEXTs and both COLLECTs. But if your macro digs up
symbols from a package you didn't write and then binds them there is
no way for the user to get back to their *own* bindings of those
symbols. Which seems quite rude.

>> Also, since the READ-FROM-STRING is going to happen at macro-expand
>> time, which is different from the read time of the code that uses
>> MAP-COLLECT, there is no guarantee (though it is, of course,
>> likely) that *PACKAGE* will be set to the same value at both times.
>> The rules requiring *PACKAGE* to be set the same when you LOAD a
>> file as when you COMPILE-FILE it probably give you some cover
>> there. But you're asking for trouble it seems to me. Don't fight
>> the language.
>
> I'm not fighting it. I've seen macros in others' code that does the
> same thing, using MAKE-SYMBOL and INTERN. I prefer READ-FROM-STRING,
> as it combines both steps into one and respects readcase.

I don't think I'm the only one that considers such macros to be poor
form. And yes it's poor form when DEFSTRUCT does it too. (Consider, as
Steve Haflich has pointed out in the past, what happens when you say
(defstruct instance ...). You probably don't want to try that for real
unless you're ready to restart Lisp because you may have to.)

-Peter

-- 
Peter Seibel                                      ·····@javamonkey.com

         Lisp is the red pill. -- John Fraser, comp.lang.lisp
From: Duane Rettig
Subject: Re: flets in macros
Date: 
Message-ID: <43by3cznx.fsf@franz.com>
Peter Seibel <·····@javamonkey.com> writes:

> Chris Capel <······@iba.nktech.net> writes:
> 
> > Peter Seibel wrote:
> >
> >> Chris Capel <······@iba.nktech.net> writes:
> >> 
> >>> I'm writing a macro, map-collect, that performs a mapping function over a
> >>> list or lists and binds COLLECT and NEXT to functions that allow the
> >>> following:
> >>>
> >>> (map-collect mapcar (item '(1 2 3 4 5 6))
> >>>   (collect item)
> >>>   (when (evenp item)
> >>>     (next)))
> >>>
> >>> ==> ((1 2) (3 4) (5 6) ())
> >>>
> >>> Now, my problem is that I'm defining this an a utility package, but I
> >>> don't want to have to put on package qualifiers to the collect and next
> >>> symbols every time I use it in other packages. I also really don't want
> >>> to write a code-walker in my macro to manually replace any symbol with
> >>> the right symbol name. So I figure I have two options. Either I export
> >>> both COLLECT and NEXT, or I do something like this:
> >>>
> >>> (defmacro map-collect (...)
> >>>   `( ...
> >>>      (flet ((,(read-from-string "collect") ...)
> >>>             (,(read-from-string "next") ...)
> >>>        ...)))
> >>>
> >>> The latter approach works, and I sort of like it. Is either preferable?
> >> 
> >> Exporting COLLECT and NEXT is preferable. COLLECT and NEXT are part of
> >> the public API of MAP-COLLECT so they should be exported just like any
> >> other names that are part of the public API. You should not pollute
> >> other folk's name spaces.
> >
> > What, you mean like LOOP does?
> 
> Well, LOOP doesn't bind the names is uses as "keywords"--it implements
> its own grammar (essentially a shallow tree walker). You can use the
> same symbol both as a LOOP keyword and for your own purposes:
> 
>   (flet ((collect (x) (format t "Collecting ~a~%" x)))
>     (loop for i from 1 to 10 collect i do (collect i)))
> 
> In other words LOOP is using the symbols purely as syntactic markers.
> If you want to use symbols as syntactic markers and then decide to
> care only about the SYMBOL-NAME of symbols being used that way, that's
> fine because you're not dorking up other folk's existing bindings.

That's true, but Chris's original post had said he did not want to
use a walker, and that's the best way to do what you are advocating.
I did not answer his original mail because his question was about
contrasting two techniques approximately equal in desirability (based
on caveats for each).  I personally think the better approach would be
to do the walk, but I can also understand his reticence to doing this...
The read-from-string style has the added distinction (you must decide
whether it's an advantage or a disadvantage) of using whatever the
current readtable-case is in effect, thus possibly resulting in more
symbols being interned (Perhaps this is what is desired).  Remember
that as Kent Pitman has often said, Common Lisp is case sensitive;
it is the reader and sometimes the printer that by default mimic
case-insensitivity through folding.  So since the reader is being
invoked in the above example, not only is the package left unspecified,
but the case of the symbol, as well.  This is simply a caveat; it may
be precisely what is desired...

> > NEXT and COLLECT and pretty common symbol names, I imagine. And
> > exporting the things are probably going to cause a lot more
> > "polluting" than otherwise. What if I have a function in my user
> > package named USER::NEXT? What happens when I want to use
> > MAP-COLLECT? I have to shadow UTILS:NEXT and write "(utils:next)" in
> > my macro body. I could make that clearer if I put MAP-COLLECT in its
> > own package, but then it's more typing to qualify it.
> 
> If you use the package system then the user has the option of not
> USE-PACKAGE'ing your package and refering to *your* NEXT and COLLECT
> with explicitly package-qualified names. At any rate at least they can
> still refer to both NEXTs and both COLLECTs. But if your macro digs up
> symbols from a package you didn't write and then binds them there is
> no way for the user to get back to their *own* bindings of those
> symbols. Which seems quite rude.

Whenever a programmer creates a new definition in a common package
(say, the user package), he is automatically encroaching on that
namespace.  If this is done in one of the implementation's packages,
then he is likely to get a warning or an error, for that reason.
The only difference between exporting a single symbol and creating
several symbols is the number of symbols involved.  If the macro is
never used while the user is within an implementation-defined package,
then both are equivalent; they will define symbols which clash with
other definitions from that package (that is why I would have preferred
the walker approach).

> >> Also, since the READ-FROM-STRING is going to happen at macro-expand
> >> time, which is different from the read time of the code that uses
> >> MAP-COLLECT, there is no guarantee (though it is, of course,
> >> likely) that *PACKAGE* will be set to the same value at both times.
> >> The rules requiring *PACKAGE* to be set the same when you LOAD a
> >> file as when you COMPILE-FILE it probably give you some cover
> >> there. But you're asking for trouble it seems to me. Don't fight
> >> the language.
> >
> > I'm not fighting it. I've seen macros in others' code that does the
> > same thing, using MAKE-SYMBOL and INTERN. I prefer READ-FROM-STRING,
> > as it combines both steps into one and respects readcase.
> 
> I don't think I'm the only one that considers such macros to be poor
> form. And yes it's poor form when DEFSTRUCT does it too. (Consider, as
> Steve Haflich has pointed out in the past, what happens when you say
> (defstruct instance ...). You probably don't want to try that for real
> unless you're ready to restart Lisp because you may have to.)

I don't know if Steve used the term "poor form", but his caveat is just
that; a caveat - it speaks to a risk of doing things in a certain way.
But please remember that Common Lisp is a language-building language,
and there is nothing in CL that precludes one from explicitly defining
a function named make-instance in one's own language; it is in fact
likely, if the language happens to be object-oriented.  The caveat is,
of course, that if you want a function named make-instance in your
target language, you must define it in your own package and shadow
the one in the common-lisp package.  Then you are free to create the
function any way you desire, and it is not only not poor form, it is
required.

The danger of using the term "poor form" is that since Common Lisp is
such an open and malleable language, it is inevitable that your
statement is going to be falsified by reasoning which makes whatever
was labelled as poor form to be in fact the best solution.  Instead,
if you want to talk about the dangers of a technique, talk about the
dangers directly; provide a list of the caveats.  Many ingenious
solutions are left untried because of perceived roadblocks in CL
that aren't really there.

[Note that my last paragraph did _not_ label as poor form the calling
of a technique "poor form" (get that?-) precisely for the same reason;
there may be times when calling something poor form is the best way
to convince someone not to do something (it is usually thick with
the context of the problem to be solved).  Instead, it is less risky
to simply list the caveats, and then let the programmer make his
choice.  So again, my last paragraph was simply a note of the caveat
of labelling something as "poor form"]

-- 
Duane Rettig    ·····@franz.com    Franz Inc.  http://www.franz.com/
555 12th St., Suite 1450               http://www.555citycenter.com/
Oakland, Ca. 94607        Phone: (510) 452-2000; Fax: (510) 452-0182   
From: Chris Capel
Subject: Re: flets in macros
Date: 
Message-ID: <10s9ehfp9rmtd9d@corp.supernews.com>
Duane Rettig wrote:

> Peter Seibel <·····@javamonkey.com> writes:
> 
>> Chris Capel <······@iba.nktech.net> writes:
>> 
>> > NEXT and COLLECT and pretty common symbol names, I imagine. And
>> > exporting the things are probably going to cause a lot more 
>> > "polluting" than otherwise. What if I have a function in my user
>> > package named USER::NEXT? What happens when I want to use
>> > MAP-COLLECT? I have to shadow UTILS:NEXT and write "(utils:next)" in
>> > my macro body. I could make that clearer if I put MAP-COLLECT in its
>> > own package, but then it's more typing to qualify it.
>> 
>> If you use the package system then the user has the option of not
>> USE-PACKAGE'ing your package and refering to *your* NEXT and COLLECT
>> with explicitly package-qualified names. At any rate at least they can
>> still refer to both NEXTs and both COLLECTs. But if your macro digs up
>> symbols from a package you didn't write and then binds them there is
>> no way for the user to get back to their *own* bindings of those
>> symbols. Which seems quite rude.
> 
> Whenever a programmer creates a new definition in a common package
> (say, the user package), he is automatically encroaching on that
> namespace.

I'm not sure how this is a reply to what Peter said. A "common package" in
this case would be the package using map-collect? 

> If this is done in one of the implementation's packages, 
> then he is likely to get a warning or an error, for that reason.
> The only difference between exporting a single symbol and creating
> several symbols is the number of symbols involved.

Agreed.

> If the macro is 
> never used while the user is within an implementation-defined package,
> then both are equivalent;

I just lost you here. When would the user ever be in an implementation-
defined package, and why?

> they will define symbols which clash with 
> other definitions from that package (that is why I would have preferred
> the walker approach).

You mean that "could potentially clash", or am I "potentially confused"?

Chris Capel
From: Duane Rettig
Subject: Re: flets in macros
Date: 
Message-ID: <4y8fvb1cl.fsf@franz.com>
Chris Capel <······@iba.nktech.net> writes:

> Duane Rettig wrote:
> 
> > Peter Seibel <·····@javamonkey.com> writes:
> > 
> >> Chris Capel <······@iba.nktech.net> writes:
> >> 
> >> > NEXT and COLLECT and pretty common symbol names, I imagine. And
> >> > exporting the things are probably going to cause a lot more 
> >> > "polluting" than otherwise. What if I have a function in my user
> >> > package named USER::NEXT? What happens when I want to use
> >> > MAP-COLLECT? I have to shadow UTILS:NEXT and write "(utils:next)" in
> >> > my macro body. I could make that clearer if I put MAP-COLLECT in its
> >> > own package, but then it's more typing to qualify it.
> >> 
> >> If you use the package system then the user has the option of not
> >> USE-PACKAGE'ing your package and refering to *your* NEXT and COLLECT
> >> with explicitly package-qualified names. At any rate at least they can
> >> still refer to both NEXTs and both COLLECTs. But if your macro digs up
> >> symbols from a package you didn't write and then binds them there is
> >> no way for the user to get back to their *own* bindings of those
> >> symbols. Which seems quite rude.
> > 
> > Whenever a programmer creates a new definition in a common package
> > (say, the user package), he is automatically encroaching on that
> > namespace.
> 
> I'm not sure how this is a reply to what Peter said. A "common package" in
> this case would be the package using map-collect? 

I mean "common in the sense that two different programmers (or one
programmer at different times) may use the same package for different
purposes.  A generic use of the word "common".

> > If this is done in one of the implementation's packages, 
> > then he is likely to get a warning or an error, for that reason.
> > The only difference between exporting a single symbol and creating
> > several symbols is the number of symbols involved.
> 
> Agreed.
> 
> > If the macro is 
> > never used while the user is within an implementation-defined package,
> > then both are equivalent;
> 
> I just lost you here. When would the user ever be in an implementation-
> defined package, and why?

Perhaps if your macro is useful, it then becomes used as an implementation
for another library, perhaps even by a lisp vendor, or at least by the
vendor in a library (where the library is in its own package and the lisp
vendor provides for package-locking of that package).

> > they will define symbols which clash with 
> > other definitions from that package (that is why I would have preferred
> > the walker approach).
> 
> You mean that "could potentially clash", or am I "potentially confused"?

I'm not sure what this question means.  To look back: I saw three potential
implementations: a read-from-string approach, an export approach, and a
walker aproach.  Walking and replacing specific symbols in the desired
context with, say, gensymmed symbols, is in my opinion preferable to
either of the other appraches, because the walker can cons up a new
symbol guaranteed not to clash with other uses of the same symbol.
The other two approaches each carry risks of usage clash.

Only you know if you are confused or not :-)

-- 
Duane Rettig    ·····@franz.com    Franz Inc.  http://www.franz.com/
555 12th St., Suite 1450               http://www.555citycenter.com/
Oakland, Ca. 94607        Phone: (510) 452-2000; Fax: (510) 452-0182   
From: Duane Rettig
Subject: Re: flets in macros
Date: 
Message-ID: <4oegrb05p.fsf@franz.com>
Duane Rettig <·····@franz.com> writes:

> ...  Walking and replacing specific symbols in the desired
> context with, say, gensymmed symbols, is in my opinion preferable to
> either of the other appraches...

I should add "without taking other requirements into consideration."

-- 
Duane Rettig    ·····@franz.com    Franz Inc.  http://www.franz.com/
555 12th St., Suite 1450               http://www.555citycenter.com/
Oakland, Ca. 94607        Phone: (510) 452-2000; Fax: (510) 452-0182   
From: Peter Seibel
Subject: Re: flets in macros
Date: 
Message-ID: <m3is6zoycp.fsf@javamonkey.com>
Duane Rettig <·····@franz.com> writes:

> Peter Seibel <·····@javamonkey.com> writes:
>
>> Chris Capel <······@iba.nktech.net> writes:
>> 
>> > Peter Seibel wrote:
>> >
>> >> Chris Capel <······@iba.nktech.net> writes:
>> >> 
>> >>> I'm writing a macro, map-collect, that performs a mapping
>> >>> function over a list or lists and binds COLLECT and NEXT to
>> >>> functions that allow the following:
>> >>>
>> >>> (map-collect mapcar (item '(1 2 3 4 5 6))
>> >>>   (collect item)
>> >>>   (when (evenp item)
>> >>>     (next)))
>> >>>
>> >>> ==> ((1 2) (3 4) (5 6) ())
>> >>>
>> >>> Now, my problem is that I'm defining this an a utility package,
>> >>> but I don't want to have to put on package qualifiers to the
>> >>> collect and next symbols every time I use it in other packages.
>> >>> I also really don't want to write a code-walker in my macro to
>> >>> manually replace any symbol with the right symbol name. So I
>> >>> figure I have two options. Either I export both COLLECT and
>> >>> NEXT, or I do something like this:
>> >>>
>> >>> (defmacro map-collect (...)
>> >>>   `( ...
>> >>>      (flet ((,(read-from-string "collect") ...)
>> >>>             (,(read-from-string "next") ...)
>> >>>        ...)))
>> >>>
>> >>> The latter approach works, and I sort of like it. Is either
>> >>> preferable?
>> >> 
>> >> Exporting COLLECT and NEXT is preferable. COLLECT and NEXT are
>> >> part of the public API of MAP-COLLECT so they should be exported
>> >> just like any other names that are part of the public API. You
>> >> should not pollute other folk's name spaces.
>> >
>> > What, you mean like LOOP does?
>> 
>> Well, LOOP doesn't bind the names is uses as "keywords"--it
>> implements its own grammar (essentially a shallow tree walker). You
>> can use the same symbol both as a LOOP keyword and for your own
>> purposes:
>> 
>>   (flet ((collect (x) (format t "Collecting ~a~%" x)))
>>     (loop for i from 1 to 10 collect i do (collect i)))
>> 
>> In other words LOOP is using the symbols purely as syntactic
>> markers. If you want to use symbols as syntactic markers and then
>> decide to care only about the SYMBOL-NAME of symbols being used
>> that way, that's fine because you're not dorking up other folk's
>> existing bindings.
>
> That's true, but Chris's original post had said he did not want to
> use a walker, and that's the best way to do what you are advocating.

I don't think it's the best way to do what *I'm* advocating. Because
I'm advocating not writing macros that clobber the bindings of symbols
not explicitly mentioned by the user of the macro. What Chris wants,
as I understand it, is to write a macro which defines a scope in which
the names "COLLECT" and "NEXT" are bound to functions that implement
some collection machinery. Now I say "COLLECT" and "NEXT" rather than
COLLECT and NEXT because the whole point of this discussion is it's
not clear which symbols with the SYMBOL-NAME's "COLLECT" and "NEXT"
should be used to refer to this macro-supplied machinery.

Let's assume for the sake of argument that the macro is defined in a
package Chris defines. And let's assume I'm going to use that macro
from a package that I defined. If Chris writes his macro to bind his
own (i.e. in his package) COLLECT and NEXT symbols and export them
then obviously those symbols will be the ones to refer to the macro
machinery.

If Chris writes his macro to do a (read-from-string "collect") and
(read-from-string "next") at macroexpansion time, he'll likely use the
symbols from whatever package is current when the code using the macro
is compiled. My package, when I used his macro in my code.

And if he uses a tree walker he has to explicitly decide which symbols
to translate into the (presumably gensymed) actual names of his
collection machinery functions. But presumably, since he could get
either of the other two options in the ways I just mentioned, the only
reason to use a tree walker is because he wants *any* use of a symbol
(or any use in a function-name position) with the SYMBOL-NAME
"COLLECT" or "NEXT" to be translated into a reference to the macro's
machinery. So that's the worst of all, from my perspective.

I think, as a general matter, it is a bad idea to write macros that
assign meanings to symbols not provided by the user of the macro when
those meanings can shadow meanings already provided by the programmer
who defined the home package of the symbols. (Thus LOOP is actually
okay in this regard because LOOP's "keywords" are only meaningful as
LOOP keywords when they appear in particular positions defined by
LOOP's grammar. Now, note that I say "as a general matter". That
doesn't mean there aren't times when these kinds of symbol-shenanigans
might be called for; just that there is (in my mind) a presumption
*against*. If Chris really wants to do this, it seems like he needs a
better reason that to avoid exporting two symbols.

> I don't know if Steve used the term "poor form", but his caveat is just
> that; a caveat - it speaks to a risk of doing things in a certain way.
> But please remember that Common Lisp is a language-building language,
> and there is nothing in CL that precludes one from explicitly defining
> a function named make-instance in one's own language; it is in fact
> likely, if the language happens to be object-oriented.  The caveat is,
> of course, that if you want a function named make-instance in your
> target language, you must define it in your own package and shadow
> the one in the common-lisp package.  Then you are free to create the
> function any way you desire, and it is not only not poor form, it is
> required.

But that, of course, is not what I'm talking about. I'm fine with
shadowing MAKE-INSTANCE, if that's what you want. And I suppose if
you've taken that precaution you can safely use (defstruct instance
...) without clobbering CL:MAKE-INSTANCE. But I still maintain that
it's poor form--i.e. the benefits obtained do not balance they costs
paid--for DEFSTRUCT to cons up new names, intern them under the
covers, and give them new definitions.

> The danger of using the term "poor form" is that since Common Lisp
> is such an open and malleable language, it is inevitable that your
> statement is going to be falsified by reasoning which makes whatever
> was labelled as poor form to be in fact the best solution.

Only if you think "poor form" is a binary notion that can be
"falsified". Calling something "poor form" clearly implies a
judgement. And making a judgement--to me anyway--implies the
possibility of making a more nuanced judgement given more specific
information. Which doesn't render the initial judgement "incorrect";
just incomplete. But all general judgements are incomplete so I can
live with that.

-Peter

-- 
Peter Seibel                                      ·····@javamonkey.com

         Lisp is the red pill. -- John Fraser, comp.lang.lisp
From: Duane Rettig
Subject: Re: flets in macros
Date: 
Message-ID: <48y7ttnpd.fsf@franz.com>
Peter Seibel <·····@javamonkey.com> writes:

> >> In other words LOOP is using the symbols purely as syntactic
> >> markers. If you want to use symbols as syntactic markers and then
> >> decide to care only about the SYMBOL-NAME of symbols being used
> >> that way, that's fine because you're not dorking up other folk's
> >> existing bindings.
> >
> > That's true, but Chris's original post had said he did not want to
> > use a walker, and that's the best way to do what you are advocating.
> 
> I don't think it's the best way to do what *I'm* advocating.

Let's talk about that.

> Because
> I'm advocating not writing macros that clobber the bindings of symbols
> not explicitly mentioned by the user of the macro. What Chris wants,
> as I understand it, is to write a macro which defines a scope in which
> the names "COLLECT" and "NEXT" are bound to functions that implement
> some collection machinery.

I agree with this part completely.  This is why I have mentioned that
Chris' macro definitions are more intrusive, even if he uses macrolet,
because it uses the general mechanism of lexically replacing or
shadowing a binding that may already be there.  It's not necessary to
do this with a walker.

> Now I say "COLLECT" and "NEXT" rather than
> COLLECT and NEXT because the whole point of this discussion is it's
> not clear which symbols with the SYMBOL-NAME's "COLLECT" and "NEXT"
> should be used to refer to this macro-supplied machinery.

Also agreed.  What we are really talking about is a possible set of
syntax trees in a domain-specific language being supported by CL.
It need not follow CL's rules.

> Let's assume for the sake of argument that the macro is defined in a
> package Chris defines. And let's assume I'm going to use that macro
> from a package that I defined. If Chris writes his macro to bind his
> own (i.e. in his package) COLLECT and NEXT symbols and export them
> then obviously those symbols will be the ones to refer to the macro
> machinery.

Of course.  Where do we disagree here?

> If Chris writes his macro to do a (read-from-string "collect") and
> (read-from-string "next") at macroexpansion time, he'll likely use the
> symbols from whatever package is current when the code using the macro
> is compiled. My package, when I used his macro in my code.

Yes.  I warned about that in my posting, and this is the difference
between the two styles Chris selected as his pair of choices.

> And if he uses a tree walker he has to explicitly decide which symbols
> to translate into the (presumably gensymed) actual names of his
> collection machinery functions.

Yes, this is the power of a code-walker.

> But presumably, since he could get
> either of the other two options in the ways I just mentioned, the only
> reason to use a tree walker is because he wants *any* use of a symbol
> (or any use in a function-name position) with the SYMBOL-NAME
> "COLLECT" or "NEXT" to be translated into a reference to the macro's
> machinery.

This is not true.  A domain-specific language might call for the
replacement of the name whenever it shows up as the SECOND in the
list (for, say, some kind of in-fix language), or it might call for
every other occurrence of the symbol to be translated (why, I wouldn't
know, but it is easly done with a code walker).  Or the replacements
might only occur when the current contour is in effect, and replacement 
might then not occur within an inner contour (e.g. a let form, a progn
form, a more domain-specific defpart form, etc.) or within forms that
constitute data (or vice-versa).  The possibilities are completely at
the control of the walker.  I would think that such specificity is
precisely why one would want a code walker, and not just to blindly
replace *all* occurrences.

> So that's the worst of all, from my perspective.

This therefore doesn't follow.

> I think, as a general matter, it is a bad idea to write macros that
> assign meanings to symbols not provided by the user of the macro when
> those meanings can shadow meanings already provided by the programmer
> who defined the home package of the symbols. (Thus LOOP is actually
> okay in this regard because LOOP's "keywords" are only meaningful as
> LOOP keywords when they appear in particular positions defined by
> LOOP's grammar.

This is _precisely_ what I'm saying.

> Now, note that I say "as a general matter". That
> doesn't mean there aren't times when these kinds of symbol-shenanigans
> might be called for; just that there is (in my mind) a presumption
> *against*. If Chris really wants to do this, it seems like he needs a
> better reason that to avoid exporting two symbols.

He stated his reason; he doesn't want the symbols to require package
qualifying.  This is how loop works, as well.

> > I don't know if Steve used the term "poor form", but his caveat is just
> > that; a caveat - it speaks to a risk of doing things in a certain way.
> > But please remember that Common Lisp is a language-building language,
> > and there is nothing in CL that precludes one from explicitly defining
> > a function named make-instance in one's own language; it is in fact
> > likely, if the language happens to be object-oriented.  The caveat is,
> > of course, that if you want a function named make-instance in your
> > target language, you must define it in your own package and shadow
> > the one in the common-lisp package.  Then you are free to create the
> > function any way you desire, and it is not only not poor form, it is
> > required.
> 
> But that, of course, is not what I'm talking about. I'm fine with
> shadowing MAKE-INSTANCE, if that's what you want. And I suppose if
> you've taken that precaution you can safely use (defstruct instance
> ...) without clobbering CL:MAKE-INSTANCE. But I still maintain that
> it's poor form--i.e. the benefits obtained do not balance they costs
> paid--for DEFSTRUCT to cons up new names, intern them under the
> covers, and give them new definitions.

I hadn't commented directly on this aspect, but I will do so now.  I
tend to agree with this.  However, defstruct is a very old construct,
and although not deprecated, it was supplanted in concept by CLOS
construction techniques.  Another major reason against automatic
construction of names is the difficulty that such techniques cause for
people looking for origins of definitions in a large system - CLOS forces
explicit accessor names, and thus avoids this problem.  However,
source-file annotations can mitigate these issues, and package locking
can mitigate the potential unintentional clobbering of names.  The
technique is in fact often used in other programming efforts, and
whether the name you create is inherently close to other potential
names or something that is not likely to clash with another name
is somewhat of an art, and is just as important as choosing good
names for any operators.

> > The danger of using the term "poor form" is that since Common Lisp
> > is such an open and malleable language, it is inevitable that your
> > statement is going to be falsified by reasoning which makes whatever
> > was labelled as poor form to be in fact the best solution.
> 
> Only if you think "poor form" is a binary notion that can be
> "falsified".

It is not necessarily a binary notion, but it tends to be taken that
way - usually any negative qualitative assessment is impossible to
communicate accurately, and it usually gets blown up to a much larger
negative than it may have been intended.  Caveats, or warnings, are
more specific; they can be measured, quantified, and possibly even
rejected if other factors take precedence.  A qualitative judgement
can lead to an equally vehement qualitative judgement in the opposite
direction, which inevitably leads to polarization.

 Calling something "poor form" clearly implies a
> judgement. And making a judgement--to me anyway--implies the
> possibility of making a more nuanced judgement given more specific
> information. Which doesn't render the initial judgement "incorrect";
> just incomplete. But all general judgements are incomplete so I can
> live with that.

It is because all general judgements are incomplete that I can't live
with them, but I advocate making specific judgements instead.

-- 
Duane Rettig    ·····@franz.com    Franz Inc.  http://www.franz.com/
555 12th St., Suite 1450               http://www.555citycenter.com/
Oakland, Ca. 94607        Phone: (510) 452-2000; Fax: (510) 452-0182   
From: Chris Capel
Subject: Re: flets in macros
Date: 
Message-ID: <10sdmjaokcbrh8d@corp.supernews.com>
Duane Rettig wrote:

> Peter Seibel wrote:
>
>> But presumably, since he could get
>> either of the other two options in the ways I just mentioned, the only
>> reason to use a tree walker is because he wants any use of a symbol
>> (or any use in a function-name position) with the SYMBOL-NAME
>> "COLLECT" or "NEXT" to be translated into a reference to the macro's
>> machinery.
> 
> This is not true.  A domain-specific language might call for the
> replacement of the name whenever it shows up as the SECOND in the
> list (for, say, some kind of in-fix language), or it might call for
> every other occurrence of the symbol to be translated (why, I wouldn't
> know, but it is easly done with a code walker).  Or the replacements
> might only occur when the current contour is in effect, and replacement
> might then not occur within an inner contour (e.g. a let form, a progn
> form, a more domain-specific defpart form, etc.) or within forms that
> constitute data (or vice-versa).  The possibilities are completely at
> the control of the walker.  I would think that such specificity is
> precisely why one would want a code walker, and not just to blindly
> replace all occurrences.

When Peter responded with his point about LOOP having a grammar, it occurred
to me that this wouldn't be practical in my map-collect, because the macro
would have no reliable way to distinguish instances of COLLECT and NEXT
that were intended to be used as macro special words, and instances that
were intended to be used in a more general way. LOOP has a very simple
criterion: the symbol has to occur at the top level of the macro. That
criterion is insufficient for map-collect--for it to be feasible, one would
have to implement control structure words, and one would end up with
another LOOP.

Now, the problem is that I can't think of *any* particular set of criteria
that would suffice in this situation. The closest I can come to useful is
to replace *all* instances of the symbol with the appropriate flet call,
which is equivalent to the (read-from-string "collect") approach. I think
this is why Peter characterized the tree-walking approach as he did--there
isn't any obviously superior way to *use* a tree-walker. You do say that
you favor the tree-walking approach, so perhaps you have more particular
and discerning criteria in mind.

Chris Capel
From: Duane Rettig
Subject: Re: flets in macros
Date: 
Message-ID: <4y8ftq7nf.fsf@franz.com>
Chris Capel <······@iba.nktech.net> writes:

> Duane Rettig wrote:
> 
> > Peter Seibel wrote:
> >
> >> But presumably, since he could get
> >> either of the other two options in the ways I just mentioned, the only
> >> reason to use a tree walker is because he wants any use of a symbol
> >> (or any use in a function-name position) with the SYMBOL-NAME
> >> "COLLECT" or "NEXT" to be translated into a reference to the macro's
> >> machinery.
> > 
> > This is not true.  A domain-specific language might call for the
> > replacement of the name whenever it shows up as the SECOND in the
> > list (for, say, some kind of in-fix language), or it might call for
> > every other occurrence of the symbol to be translated (why, I wouldn't
> > know, but it is easly done with a code walker).  Or the replacements
> > might only occur when the current contour is in effect, and replacement
> > might then not occur within an inner contour (e.g. a let form, a progn
> > form, a more domain-specific defpart form, etc.) or within forms that
> > constitute data (or vice-versa).  The possibilities are completely at
> > the control of the walker.  I would think that such specificity is
> > precisely why one would want a code walker, and not just to blindly
> > replace all occurrences.
> 
> When Peter responded with his point about LOOP having a grammar, it occurred
> to me that this wouldn't be practical in my map-collect, because the macro
> would have no reliable way to distinguish instances of COLLECT and NEXT
> that were intended to be used as macro special words, and instances that
> were intended to be used in a more general way. LOOP has a very simple
> criterion: the symbol has to occur at the top level of the macro. That
> criterion is insufficient for map-collect--for it to be feasible, one would
> have to implement control structure words, and one would end up with
> another LOOP.

I assume you mean another domain-specific language that looks like
LOOP.  (You can in fact define a walker that selects different criteria
for matching symbols - it doesn't need to be restricted to the
top-level.)

Yes, the names you are using in your design are fairly common, and
that makes the job of distinguishing them from other uses harder
(not impossible, though).  Another way to disinguish the names
is to label them with a less-likely name, such as mc-collect
and mc-next.  That requires a little more typing, but it tends
to disambiguate more throughly than using a common name.

> Now, the problem is that I can't think of *any* particular set of criteria
> that would suffice in this situation. The closest I can come to useful is
> to replace *all* instances of the symbol with the appropriate flet call,
> which is equivalent to the (read-from-string "collect") approach.

That depends on what you mean by "the symbol".  The read-from-string
approach will effectively replace all symbols accessible in
the _current_ package without qualification.  An export/macrolet
approach would replace symbols in the package that defined the
macrolet.  Neither are completely equivalent to replacement of *all*
symbols with the same symbol-name by a walker.

[Note that I am taking (and repeating) your terminology "replace
the symbol" as a shortcut for saying "replace the form whose operator
is the symbol"]

> I think
> this is why Peter characterized the tree-walking approach as he did--there
> isn't any obviously superior way to *use* a tree-walker. You do say that
> you favor the tree-walking approach, so perhaps you have more particular
> and discerning criteria in mind.

Well, as I was following this discussion, I was picking up these
criteria from what you seemed to be desiring (but perhaps I
misunderstood your criteria):

 1. You wanted not to have to qualify package names in order to get
the correct replacement.  Both of your two techniques meet this
criterion to a certain extent, but not 100%.  Both require that
the symbol be accessible in some package and that that accessibility
match the symbol being defined.  One technique holds the symbol to
a fixed package, and the other floats the symbol to the current
package.  Neither are keyed on precisely the symbol-name.

 2. You didn't want to use a walker.  This is why I didn't answer
you; your question had to do with two techniques that can be assessed
as having caveats of similar magnitude.

 3. You and Peter started discussing the use of the terms "collect"
and "next" as syntactic markers, and it was you that brought up
LOOP.  It was when Peter started describing what LOOP does that
I jumped in, stating that you had already rejected the use of a
code-walker that would have provided such syntactic marking.

You don't say why you don't want to use a code-walker.  Perhaps
you can clarify that, but I took your original criteria at face
value.  If you were to open up the possibilities by considering
one, then you would have more tools to work with.

-- 
Duane Rettig    ·····@franz.com    Franz Inc.  http://www.franz.com/
555 12th St., Suite 1450               http://www.555citycenter.com/
Oakland, Ca. 94607        Phone: (510) 452-2000; Fax: (510) 452-0182   
From: Chris Capel
Subject: Re: flets in macros
Date: 
Message-ID: <10s9dqbbnhmlt4c@corp.supernews.com>
Peter Seibel wrote:

> Chris Capel <······@iba.nktech.net> writes:
> 
>> NEXT and COLLECT and pretty common symbol names, I imagine. And
>> exporting the things are probably going to cause a lot more
>> "polluting" than otherwise. What if I have a function in my user
>> package named USER::NEXT? What happens when I want to use
>> MAP-COLLECT? I have to shadow UTILS:NEXT and write "(utils:next)" in
>> my macro body. I could make that clearer if I put MAP-COLLECT in its
>> own package, but then it's more typing to qualify it.
> 
> If you use the package system then the user has the option of not
> USE-PACKAGE'ing your package and refering to *your* NEXT and COLLECT
> with explicitly package-qualified names. At any rate at least they can
> still refer to both NEXTs and both COLLECTs. But if your macro digs up
> symbols from a package you didn't write and then binds them there is
> no way for the user to get back to their *own* bindings of those
> symbols. Which seems quite rude.

There's no way to selectively ignore certain lexical bindings in Lisp, and
access the shadowed bindings? Hmm. In C#, you can use the "this" and "base"
variables to disambiguate local variables, class members, and method
overloads. Not a complete solution, but better than nothing. And besides
that, there are a lot of kludges. Like not being able to shadow variables
from a parent scope in a child scope. (B&D feature.) So that doesn't prove
there could be a nice way to provide that feature, but it wouldn't appear
to be too difficult. Was it just not an in-demand feature?

> I don't think I'm the only one that considers such macros to be poor
> form.

Well, given that such a solution would leave a user without a way to access
their global functions named COLLECT or NEXT within my macro, I would say I
have to agree. But it's a shame.

Chris Capel
From: Wade Humeniuk
Subject: Re: flets in macros
Date: 
Message-ID: <1ZXwd.5675$KO5.4054@clgrps13>
Chris Capel wrote:
> I'm writing a macro, map-collect, that performs a mapping function over a
> list or lists and binds COLLECT and NEXT to functions that allow the
> following:
> 
> (map-collect mapcar (item '(1 2 3 4 5 6))
>   (collect item)
>   (when (evenp item)
>     (next)))
> 
> ==> ((1 2) (3 4) (5 6) ())
> 
> Now, my problem is that I'm defining this an a utility package, but I don't
> want to have to put on package qualifiers to the collect and next symbols
> every time I use it in other packages. I also really don't want to write a
> code-walker in my macro to manually replace any symbol with the right
> symbol name. So I figure I have two options. Either I export both COLLECT
> and NEXT, or I do something like this:
> 
> (defmacro map-collect (...)
>   `( ...
>      (flet ((,(read-from-string "collect") ...)
>             (,(read-from-string "next") ...)
>        ...)))
> 
> The latter approach works, and I sort of like it. Is either preferable?
> 

Many times I deliver LW applications and I strip out the reader from the
image.  So that functionality is no longer in some Lisp images.  I
would prefer using intern as it closer to the core of the language.

so...

(flet ((,(intern (string 'collect)) ...)
        (,(intern (string 'next)) ...)
    ...)

Since you are using flet, one overrides any global/package definitions of
collect and next, which means one does not have to worry about namespace
issues.  Just make the users aware of the fact.

The only other issue I can think of is whether you want collect and next
to work in any functions called from inside the map-collect.
Then you need some special vars to hold the functions doing the work
and defun'ed collect and next.

(defmacro map-collect (...)
    (let ((*collect-func* (lambda (....)))
          (*next-func* (lambda (....))))
       ...))

(map-collect ...
     (some-outside-func ..))

(defun some-outside-func (...)
    (collect ...))

and

(defun collect (...)
    (funcall *collect-func* ...))
(defun next (...)
    (funcall *next-func* ...))

Then you will have to export them.

Wade
From: Chris Capel
Subject: Re: flets in macros
Date: 
Message-ID: <10s9dbo5kdl6k35@corp.supernews.com>
Wade Humeniuk wrote:

> Chris Capel wrote:
>> Now, my problem is that I'm defining this an a utility package, but I
>> don't want to have to put on package qualifiers to the collect and next
>> symbols every time I use it in other packages. I also really don't want
>> to write a code-walker in my macro to manually replace any symbol with
>> the right symbol name. So I figure I have two options. Either I export
>> both COLLECT and NEXT, or I do something like this:
>> 
>> (defmacro map-collect (...)
>>   `( ...
>>      (flet ((,(read-from-string "collect") ...)
>>             (,(read-from-string "next") ...)
>>        ...)))
>> 
>> The latter approach works, and I sort of like it. Is either preferable?
>> 
> 
> Many times I deliver LW applications and I strip out the reader from the
> image.  So that functionality is no longer in some Lisp images.

Ahh, but you're usually not going to be compiling (macroexpanding) anything
after you've delivered the package, are you? All macroexpansion should
already be done, it seems like. Otherwise, you'll probably be including the
reader.

> I would prefer using intern as it closer to the core of the language.

Is this an aesthetic judgment? My own view is that read-from-string is
closer to the user of the macro's own code, because it allows their reader
to affect the way the macro's symbols are read in. On the other hand, the
macro name itself isn't going to be affected that way, unless they've bound
their reader specially while loading my library code. Hmm.

> The only other issue I can think of is whether you want collect and next
> to work in any functions called from inside the map-collect.
> Then you need some special vars to hold the functions doing the work
> and defun'ed collect and next.

Thanks for pointing this out.

Chris Capel
From: Paul F. Dietz
Subject: Re: flets in macros
Date: 
Message-ID: <K8adnXzRq989IlncRVn-2Q@dls.net>
Chris Capel wrote:

>All macroexpansion should
> already be done, it seems like. Otherwise, you'll probably be including the
> reader.

Huh?  Since when does macroexpansion necessarily involve the reader?

	Paul
From: Duane Rettig
Subject: Re: flets in macros
Date: 
Message-ID: <4u0qjb09z.fsf@franz.com>
"Paul F. Dietz" <·····@dls.net> writes:

> Chris Capel wrote:
> 
> >All macroexpansion should
> > already be done, it seems like. Otherwise, you'll probably be including the
> > reader.
> 
> Huh?  Since when does macroexpansion necessarily involve the reader?

One of his example macros explicitly calls read-from-string at
macroexpand-time, and in order to use that one, the reader would
have to be present.

-- 
Duane Rettig    ·····@franz.com    Franz Inc.  http://www.franz.com/
555 12th St., Suite 1450               http://www.555citycenter.com/
Oakland, Ca. 94607        Phone: (510) 452-2000; Fax: (510) 452-0182   
From: Chris Capel
Subject: Re: flets in macros
Date: 
Message-ID: <10s9h912l01av38@corp.supernews.com>
Paul F. Dietz wrote:

> Chris Capel wrote:
> 
>>All macroexpansion should
>> already be done, it seems like. Otherwise, you'll probably be including
>> the reader.
> 
> Huh?  Since when does macroexpansion necessarily involve the reader?

Since when do you need to macroexpand things when you don't need to compile
any top-levels forms with macros in them? Since when do you compile things
without reading them in first? OK, the link isn't absolutely necessary, but
I can't think of any normal circumstance where you'd want the compiler but
not the reader. Or the macroexpander, but not the reader. But would you
ever need the macroexpander without the compiler?

Chris Capel
From: Paul F. Dietz
Subject: Re: flets in macros
Date: 
Message-ID: <YuydnUZpu7bZWlncRVn-rA@dls.net>
Chris Capel wrote:

>>Huh?  Since when does macroexpansion necessarily involve the reader?
> 
> Since when do you need to macroexpand things when you don't need to compile
> any top-levels forms with macros in them?   Since when do you compile things
> without reading them in first? OK, the link isn't absolutely necessary, but
> I can't think of any normal circumstance where you'd want the compiler but
> not the reader.

I use macros that don't invoke the reader almost every day.

You can build up forms in lisp without invoking the reader at all, and
call either EVAL or COMPILE on them (either of which can then cause
macroexpansion to occur.)  I also do this almost every day.

	Paul