From: Pascal Costanza on
Spiros Bousbouras wrote:
> On 10 Nov, 01:40, p...(a)informatimago.com (Pascal J. Bourguignon)
> wrote:
>> Spiros Bousbouras <spi...(a)gmail.com> writes:
>>> On 7 Nov, 19:04, Pascal Costanza <p...(a)p-cos.net> wrote:
>>>> One important thing to keep in mind: Don't use side effects in macro
>>>> definitions. In both your solutions, you tried to achieve the result by
>>>> assigning to a local or global variable as part of the macroexpansion
>>>> process. However, you cannot predict whether or when, and how often, a
>>>> particular macro will be invoked. For example, development environments
>>>> may offer users to expand macros on demand, compilers may expand macros
>>>> several times for analysis, higher-order macros may expand inner macro
>>>> for analysis as well, etc., etc. So make sure that your macros are
>>>> always side-effect-free (unless you really know what you're doing).
>>> So you're saying that setq (or setf) should not appear in the code of
>>> the macroexpansion function ? Why , what could go wrong ?
>> (defvar *c* 0)
>>
>> (defmacro m ()
>> (incf *c*)
>> `',*c*)
>
> If that's what Pascal meant I'm not surprised. But he referred to the
> code in my first post, for example
>
> (let ((a 0))
> (defmacro memory (&rest body)
> (prog2
> (incf a)
> `(symbol-macrolet ((cell ,a))
> ,@body)
> (decf a))))
>
> His phrasing above and the example gave me the impression that he was
> saying that all assignments are dangerous even if the effects of the
> assignment do not persist after the macroexpansion function has
> finished executing. If that's what he meant then I do find it
> surprising. For example I don't see what could go wrong with my code
> above regardless of how many times the macro gets expanded.

I didn't read your code carefully enough, so I indeed didn't notice that
the side effect didn't persist. However, this code could still create
problems, for example when a user tries to macroexpand some particular
form while some other code is compiled in a background thread, and two
invocations of 'memory are thus expanded at the same time. You would
either need to synchronize the accesses to the 'a variable, for example
by using locks or STM (har :P ), or you should just use local bindings
for 'a.

> In any case, it seems that if one wants to have macros share
> information (apart from trivial cases perhaps) then this cannot be
> achieved behind the scenes so to speak. What I mean behind the scenes
> is the way you would do it with functions:
>
> (let ((a))
> (defun foo ...
> (defun bar ...)
>
> Any changes to a one function makes the other knows about but the
> rest of the code is ignorant about a. With macros however one has to
> put any changes to the shared information in the code the macro
> produces thereby leaking what ought to be internal implementation. I
> consider this a blemish in Lisp.

You can hide the shared information in a package.


Pascal

--
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
From: Thomas A. Russ on
Spiros Bousbouras <spibou(a)gmail.com> writes:

> If that's what Pascal meant I'm not surprised. But he referred to the
> code in my first post, for example
>
> (let ((a 0))
> (defmacro memory (&rest body)
> (prog2
> (incf a)
> `(symbol-macrolet ((cell ,a))
> ,@body)
> (decf a))))
>
> His phrasing above and the example gave me the impression that he was
> saying that all assignments are dangerous even if the effects of the
> assignment do not persist after the macroexpansion function has
> finished executing. If that's what he meant then I do find it
> surprising. For example I don't see what could go wrong with my code
> above regardless of how many times the macro gets expanded.

Well, I'm not convinced that this has to work the way you may wish it
to. In particular, the expansion of macros inside BODY doesn't have to
occur in the dynamic scope of the expansion of MEMORY.

So, for example, it seems to me it is perfectly permissible, and in fact
the most likely implementation, that when macros are being expanded the
following happens:

The macro MEMORY is encountered by the code walker or compiler. It is
expanded, and given the BODY contents. The BODY contents are not
evaluated, but merely returned inside the SYMBOL-MACROLET form. The
macroexpansion function for MEMORY returns the new form.

This new form is now walked by the code walker, so if there is any
macros in the newly returned form, they will also be properly expanded.
But since the macro-expansion function has already returned, the value
of variable A has been restored. So the next expansion doesn't see any
dynamic change.

In fact, I'm pretty sure it has to work this way, since otherwise macros
that expand into code headed by other macros would not work properly.
There will not be any invocation of macro-expansion functions during the
invocation of a macro-expansion function.

You could probably observe this by tracing the macro-expansion function
that corresponds to your macro. I would expect to see it return before
any other macro expansion occurs.


--
Thomas A. Russ, USC/Information Sciences Institute
From: Tobias C. Rittweiler on
Spiros Bousbouras <spibou(a)gmail.com> writes:

> (let ((a 0))
> (defmacro memory (&rest body)
> (prog2
> (incf a)
> `(symbol-macrolet ((cell ,a))
> ,@body)
> (decf a))))

(Mind Thoms Russ' response!)


> In any case, it seems that if one wants to have macros share
> information (apart from trivial cases perhaps) then this cannot be
> achieved behind the scenes so to speak. What I mean behind the scenes
> is the way you would do it with functions:
>
> (let ((a))
> (defun foo ...
> (defun bar ...)
>
> Any changes to a one function makes the other knows about but the
> rest of the code is ignorant about a. With macros however one has to
> put any changes to the shared information in the code the macro
> produces thereby leaking what ought to be internal implementation.

Where is it leaking? Because you can introspectively look at a macro's
expansion? Do you consider reflection to be "blemish" in general?


> I consider this a blemish in Lisp.

Impenetrable encapsulation is only longed for by the timid. :-)

-T.
From: Spiros Bousbouras on
On 11 Nov, 00:40, Pascal Costanza <p...(a)p-cos.net> wrote:
> Spiros Bousbouras wrote:
>
> > (let ((a 0))
> > (defmacro memory (&rest body)
> > (prog2
> > (incf a)
> > `(symbol-macrolet ((cell ,a))
> > ,@body)
> > (decf a))))
>
> > His phrasing above and the example gave me the impression that he was
> > saying that all assignments are dangerous even if the effects of the
> > assignment do not persist after the macroexpansion function has
> > finished executing. If that's what he meant then I do find it
> > surprising. For example I don't see what could go wrong with my code
> > above regardless of how many times the macro gets expanded.
>
> I didn't read your code carefully enough, so I indeed didn't notice that
> the side effect didn't persist. However, this code could still create
> problems, for example when a user tries to macroexpand some particular
> form while some other code is compiled in a background thread, and two
> invocations of 'memory are thus expanded at the same time.

Is there any established way for how such parallel
expansion/compilation is supposed to work ? Intuitively it seems to
me that in the scenario you're describing the implementation ought to
create one copy of a for the compilation and another for any
expansions which are not part of the compilation. If it doesn't do
that what makes you think that even your code will work correctly ?
There may be clashes with the use of new-cell-value.

> > In any case, it seems that if one wants to have macros share
> > information (apart from trivial cases perhaps) then this cannot be
> > achieved behind the scenes so to speak. What I mean behind the scenes
> > is the way you would do it with functions:
>
> > (let ((a))
> > (defun foo ...
> > (defun bar ...)
>
> > Any changes to a one function makes the other knows about but the
> > rest of the code is ignorant about a. With macros however one has to
> > put any changes to the shared information in the code the macro
> > produces thereby leaking what ought to be internal implementation. I
> > consider this a blemish in Lisp.
>
> You can hide the shared information in a package.

You can but still I find it kludgy. You already have the datum in a
variable i.e. in memory but in order to share it with a different
invocation of the macro you have to "print" it and then read it back
in again. Very ugly. Apart from aesthetics there's another issue :
"Practical Common Lisp" says in footnote 4 of chapter 4 : "Not all
Lisp objects can be written out in a way that can be read back in".
What if it is such an object you want to share ?

--
Never attribute to conspiracy what can adequately be explained by
shared attitudes.
From: Pillsy on
On Nov 10, 4:36 pm, Spiros Bousbouras <spi...(a)gmail.com> wrote:
[...]
> In any case, it seems that if one wants to have macros share
> information (apart from trivial cases perhaps) then this cannot be
> achieved behind the scenes so to speak. What I mean behind the scenes
> is the way you would do it with functions:

> (let ((a))
>    (defun foo ...
>    (defun bar ...)

> Any changes to a one function makes the other knows about but the
> rest of the code is ignorant about a.

This is a trick I've used once or twice in the past, and I'm just
submitting it as an idea in part because I'd like to see if there are
problems with it that I haven't considered up until now. It's hardly
something I do every day, and I know this corner of macrology is
generally riddled with pitfalls.

The idea is that you have easy access to uninterned symbols, and those
symbols not only have their value cells, but also function cells and
property lists to play with. You could do something like this:

(let ((closed (closed "BACKING-")))
(defmacro foo (name &rest args)
(setf (get closed name) (stuff-from-foo-args args))
(generate-foo-expansion name args closed))
(defmacro bar (name &rest args)
(setf (get closed name) (stuff-from-bar-args args))
(generate-bar-expansion name args closed)))

Is there something horribly wrong with this approach?

Cheers,
Pillsy
First  |  Prev  |  Next  |  Last
Pages: 1 2 3 4 5 6 7 8
Prev: Lisp sucks!
Next: grabbing key presses