From: Spiros Bousbouras on
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 ?

--
Who's your mama ?
From: Pascal J. Bourguignon on
Spiros Bousbouras <spibou(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*)

(defun f ()
(list (m) (m)))

(f) --> (1 2) ; or
--> (42 87) ; or anything else.

(f) --> (1 2) ; or
--> (3 4) ; or
--> (36 99) ; or anything else.


For example, here is what you get with clisp:

C/USER[5]> (f) ; interpreted
(1 2)
C/USER[6]> (f) ; minimal compilation is cached.
(1 2)
C/USER[7]> (compile 'f) ; execute minimal compilation once again
F ;
NIL ;
NIL
C/USER[8]> (f) ; hence the new result
(3 4)
C/USER[9]> (f)
(3 4)


--
__Pascal Bourguignon__
http://www.informatimago.com
From: Pascal Costanza on
Spiros Bousbouras wrote:
> On 7 Nov, 19:04, Pascal Costanza <p...(a)p-cos.net> wrote:
>> What do you want the following to evaluate to?
>
> No preference , the question was for educational purposes.
>
>> (setq f (memory
>> (lambda ()
>> (memory (print cell)))))
>>
>> (funcall f)
>>
>> If you want this to evaluate to 1, the other Pascal's solution is fine.
>>
>> IF you want this to evaluate to 2, here is another solution:
>>
>> (define-symbol-macro cell 0)
>>
>> (defmacro memory (&body body &environment env)
>> (let ((new-cell-value (1+ (macroexpand 'cell env))))
>> `(symbol-macrolet ((cell ,new-cell-value))
>> ,@body)))
>
> This is interesting. I did some experiments trying to understand why
> it behaves the way it does. Let's also define
>
> (defmacro memory2 (&body body)
> (let ((new-cell-value (1+ (macroexpand 'cell))))
> `(symbol-macrolet ((cell ,new-cell-value))
> ,@body)))
>
> (let ((f1
> (memory
> (lambda ()
> (memory (format t "Calling f1 gives ~a~%" cell)))))
> (f2
> (memory2
> (lambda ()
> (memory2 (format t "Calling f2 gives ~a~%" cell))))))
> (funcall f1)
> (funcall f2)
> '| |)
>
> I get
> Calling f1 gives 2
> Calling f2 gives 1
>
> Let me see if I understand the reason for the difference between
> memory and memory2. The first call to the two macros expands to the
> same thing and places what follows in the scope of (after expansions)
> (symbol-macrolet ((cell 1))
> The difference lies in the behaviour of the line
> (let ((new-cell-value (1+ (macroexpand 'cell env))))
> vs
> (let ((new-cell-value (1+ (macroexpand 'cell))))
> in the second call to the macros. There
> (macroexpand 'cell env) evaluates to 1 whereas
> (macroexpand 'cell) evaluates to 0.
> The reason is the following excerpt in the macroexpand page in
> the HS:
>
> In addition to macro definitions in the global environment,
> any local macro definitions established within env by
> macrolet or symbol-macrolet are considered. If only form is
> supplied as an argument, then the environment is effectively
> null, and only global macro definitions as established by
> defmacro are considered.
>
> I assume the last line should actually read "defmacro or
> define-symbol-macro are considered".
>
> Is my understanding correct ?

Yes, this is correct. The Common Lisp environment objects effectively
give you a first-class representation of (compile-time) lexical
environments.


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: Pascal Costanza on
Spiros Bousbouras wrote:
> 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 ?

As I said, a macro may be expanded more than once. For example, in the
LispWorks IDE (and others), I can right-click on an s-expression and
macroexpand it, to see what the result of macroexpansion will be. This
will trigger any side effects the macro definition may produce.

Here is another example: Assume you have a 'with-locked macro that
supposedly takes a lock on an object and then executes some code. Since
taking locks is expensive, it may want to avoid doing so. So it does
something like this:

(defmacro with-lock (object &body body &environment env)
(if (contains object (walk body env))
`(call-with-locked-object ,object (lambda () ,@body))
`(progn ,@body)))

(defun contains (object expanded-body)
... some form of membership test ...)

(defun walk (code env)
... something that repeatedly calls
(macroexpand code env)
to expand all macros in code
...)

> Who's your mama ?

I don't know why that would be relevant in this thread.


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: Spiros Bousbouras on
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.

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.

--
Capitalization is the difference between "I had to help my uncle Jack
off a horse.." and "I had to help my uncle jack off a horse.."
http://www.bash.org/?367896
First  |  Prev  |  Next  |  Last
Pages: 1 2 3 4 5 6 7 8
Prev: Lisp sucks!
Next: grabbing key presses