define-macro: new behind-the-curtains behavior

Started by Excalibor, September 22, 2004, 11:19:46 AM

Previous topic - Next topic

Excalibor

Hi!



I've been reading and thinking a bit...



how hard would it be for define-macro to automagically switch to a new context each time it's called? This would avoid variable collision in a very elegant way, even if multiple expansions are being performed...



I gotta go now, but I'l post an example when I'm back home in an hour or so but the idea is:



(define-macro (foo x) (begin (set 'M (symbol (random)))(context 'M)(*macro goes in here *)(context 'MAIN)))

(set 'x 1)

(print (foo 45)) ; in here 45 is referred by 'M:x and M is different everytime (or it should be) so it's not posible that the expansions collide...

(print x) ; => prints 1



well i don't seem to make a good enough example in such a rush, will make later



regards,

david

Lutz

#1
A Context switch only affects compiling statements like 'load', and 'eval-string'.    For this reason a context statement only on the toplevel where it is evaluated when loading will affect the translation of symbols to follow. I.e.



(define (foo)
  (context 'Ctx)
  (set 'x 123)
  (eval-str "(set 'y 456"))
  (context 'MAIN))

(foo)

x => 123
Ctx:y 456


In this example 'x' will still be part of main, only 'y' is in Ctx, because when

'foo' is executed 'x' is already compiled in to the MAIN context.



If you want to force the macro arg vars into another context you could just do:



(define-macro (foo m:x)
  (func m:x))


Lutz

Excalibor

#2
Lutz,



Now I see how it works, thanks for the clarification... (I couldn't log in at the end last night)...



My comments are motivated by this:


Quote"capture"

I took a quick look at it. It looked to me as if the way you're supposed to avoid variable capture is to use variables with unusual names. Is this really the plan? What about expansions that occur within expansions?



I'm going to be beaten to the punch by another Lisp dialect, can we make it Goo?

By Paul Graham at Wed, 09/15/2004 - 14:57


that was published in http://lambda-the-ultimate.org/node/view/257#comment">http://lambda-the-ultimate.org/node/view/257#comment as announced in the newLISP news forum in the Paul Graham is listing newLISP thread...



My idea goal is to avoid name collisions on variable expansions... Would it be too difficult to make (context) work inside macros?



something similar to this:


> (set 'x 13)
13
> (define (foo a , x) (set 'x a) (println x))
(lambda (a , x) (set 'x a) (println x))
> (foo 8)
8
8
> x
13


but that happened automatically? I thought contexts (random contexts) could work, because something similar is done in Tcl when you are toying around with unknown, and other interesting functions...



just some thoughts,

david

Lutz

#3
Variable capture is a potential danger in any dynamiccally soped language but only a potential problem in macros. In normal functions this is not of concern because the LISP evaluation algorithm saves the current variable environment on a stack and restores at function exit. This is what your example shows. Even if you would do (foo 'a) or (foo 'x) your function will still work correctly.



Where variable capture or name clash is a potential problem is in macros:



(define-macro (my-setq x y) (set x (eval y))) ;; bad definition



(my-setq x 123) => 123

x => nil ; x is still nil because of name clash



(my-setq z 123) => 123

z => 123 ; works becuase no name clash



(define-macro (my-setq _x _y) (set _x (eval _y))) ;; better to avoid name clashes



In my own work I never had a problem with variable capture and I have never seen code here on the discussion board affected by this.



If it is of concern in a specific situation, put your macros inside a context, which is lexically isolated or adhere to a naming convention for variables in macros. This is why all packages/modules shipped with newLISP are inside contexts, because nobody should have to look into them and should be able to treat them as black boxes.



Last not least dynamic scoping as also advantages i.e. in Aspect Oriented Programming. Nigel posted and interesting link a while ago to an article by Pascal Constanza "Dynamically Scoped Functions as the Essence of AOP". Unfortunately the link doesn't work anymore, but I have the article and can upload it, if anybody is intereseted.



Lutz

Excalibor

#4
Quote from: "Lutz"
(define-macro (my-setq _x _y) (set x (eval y))) ;; better to avoid name clashes


I guess you meant: (define-macro (my-setq _x _y)(set _x (eval _y)))



right?


Quote
In my own work I never had a problem with variable capture and I have never seen code here on the discussion board affected by this.



If it is of concern in a specific situation, put your macros inside a context, which is lexically isolated or adhere to a naming convention for variables in macros. This is why all packages/modules shipped with newLISP are inside contexts, because nobody should have to look into them and should be able to treat them as black boxes.



Last not least dynamic scoping as also advantages i.e. in Aspect Oriented Programming. Nigel posted and interesting link a while ago to an article by Pascal Constanza "Dynamically Scoped Functions as the Essence of AOP". Unfortunately the link doesn't work anymore, but I have the article and can upload it, if anybody is intereseted.



Lutz


Please, do post it, it sounds very interesting...



Regarding the macros, imaging I want to write a foreach macro similar to CL for...:



(define-macro (foreach _x _from "from" _to "to" _foo)
  (for (_x '_from '_to)
    (_foo '_x)))


or similar, as this doesn't compile, probably due to the "from" and "to" strings... how does this work in newLISP?



thanks,

david

Lutz

#5
You can find thr article in http://newlisp.org/downloads/various/pascal_constanza_dynfun.pdf">http://newlisp.org/downloads/various/pa ... dynfun.pdf">http://newlisp.org/downloads/various/pascal_constanza_dynfun.pdf



This could be a start for a foeeach macro:



(define-macro (foreach _x from _a to  _z _body)
  (for (_x (eval _a) (eval _z)) (eval _body)))

(foreach x from 1 to 10 (println x))

1
2
...
10


Its doesn't check that 'from' really is 'from' etc. but it works.  The (eval a) and (eval z) makes sure that you can say:



(foreach i from start to end (println i)



When start is a number etc.



Lutz

ps: added underscores later

HPW

#6
We had foreach long time ago here and on the old forum:



http://www.alh.net/newlisp/phpbb/viewtopic.php?p=15&highlight=foreach#15">http://www.alh.net/newlisp/phpbb/viewto ... foreach#15">http://www.alh.net/newlisp/phpbb/viewtopic.php?p=15&highlight=foreach#15
Hans-Peter