Prev: Lisp sucks!
Next: grabbing key presses
From: Spiros Bousbouras on 9 Nov 2009 16:50 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 9 Nov 2009 20:40 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 10 Nov 2009 04:04 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 10 Nov 2009 04:10 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 10 Nov 2009 16:36
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 |