From: <Joe...@t-...> - 2017-12-19 16:20:28
|
Sam, I think I've solved the problem of initialization of LOOP forms -- in my head. I need time to write it down and show you the results. No need for lazy-let. I've not yet attacked the problem of iteration in LOOP. I'm afraid there will be no way to make everybody happen w.r.t. the value of variables in FINALLY clauses. But at least I believe we can make everyone happy w.r.t. the semantics of clauses such as FOR vars ON vars, in all tricky combinations (ordering of clauses, joining with AND). Regards, Jörg |
From: <Joe...@t-...> - 2017-12-20 17:35:54
|
Hi, Sam wrote: >"lazy-binding": bind the variable but not initialize its value. until it is explicitly set, >accessing it reaches up in the environment. Interesting challenge :-) > This would require something like `lazy-let` with the following test case: --8<---------------cut here---------------start------------->8--- (let ((a 1)) (lazy-let (a b) (print a) ; prints 1 (print b) ; error - unbound variable (setq a 10) ; inner binding of a is activated (print a) ; prints 10 (setq b 22)) ; binding of b is activated (print b) ; error - unbound variable (print a)) ; prints 1 --8<---------------cut here---------------end--------------->8--- >Alternatively, `lazy-let` can be implemented as something like > (let ((a (if (boundp 'a) a (sys::%unbound)))) ...) Certainly not, BOUNDP would only work with variables declared special. Perhaps SYMBOL-MACROLET? But that won't work either, because the problem remains that once `a becomes a symbol-macro, one cannot express anymore "let me access `a from the outer environment". Didn't somebody once post a bug report about similar infinite macro expansion about regular macros? This makes me remember that there have been several past attempts at trying to make the environment-access functions of CLtL2 fly. But Google turned up few references, I must have been dreaming. :-( http://clast.sourceforge.net/ Hmm, there's perhaps nothing that another level of delayed evaluation in order to get the desired scoping couldn't solve, so here it is: (defmacro lazy-let1 ((var) &body body) (let ((shadow (gensym (symbol-name var)))) `(let ((,shadow 'unbound));(sys::%unbound) (flet ((access-a () (if (eq ,shadow 'unbound) ,var ,shadow)) ((setf access-a) (x) (setf ,shadow x))) (symbol-macrolet ((,var (access-a))) .,body))))) ; bonus point 0: Add more required gensyms ; bonus point 1: Support a list of variables as per Sam's specification ; bonus point 2: See if LET can work with clisp's %unbound trick ; bonus point 3.5: See if you can make it work with special variables... (let ((a 1)) (lazy-let1 (a) (print a) ; prints 1 (setq a 10) ; inner binding of a is activated (print a)) ; prints 10 (print a)) ; prints 1 Regards, Jörg |
From: Sam S. <sd...@gn...> - 2017-12-20 18:12:30
|
Hi, > * <Wbret-Plevy.Ubruyr@g-flfgrzf.pbz> [2017-12-20 17:35:44 +0000]: > >> This would require something like `lazy-let` with the following test case: > > (let ((a 1)) > (lazy-let (a b) > (print a) ; prints 1 > (print b) ; error - unbound variable > (setq a 10) ; inner binding of a is activated > (print a) ; prints 10 > (setq b 22)) ; binding of b is activated > (print b) ; error - unbound variable > (print a)) ; prints 1 > >>Alternatively, `lazy-let` can be implemented as something like >> (let ((a (if (boundp 'a) a (sys::%unbound)))) ...) > Certainly not, BOUNDP would only work with variables declared special. of course - that's the point > Hmm, there's perhaps nothing that another level of delayed evaluation > in order to get the desired scoping couldn't solve, so here it is: > > (defmacro lazy-let1 ((var) &body body) > (let ((shadow (gensym (symbol-name var)))) > `(let ((,shadow 'unbound));(sys::%unbound) > (flet ((access-a () (if (eq ,shadow 'unbound) ,var ,shadow)) > ((setf access-a) (x) (setf ,shadow x))) > (symbol-macrolet ((,var (access-a))) .,body))))) > > ; bonus point 0: Add more required gensyms > ; bonus point 1: Support a list of variables as per Sam's specification > ; bonus point 2: See if LET can work with clisp's %unbound trick > ; bonus point 3.5: See if you can make it work with special variables... > > (let ((a 1)) > (lazy-let1 (a) > (print a) ; prints 1 > (setq a 10) ; inner binding of a is activated > (print a)) ; prints 10 > (print a)) ; prints 1 Wow, I am impressed. However, I am not sure CLISP will compile that to good code. I think the first use of (setf access-a) should modify both (setf access-a) and access-a to be trivial operations. This is why I think a new special form TAGLET is necessary. -- Sam Steingold (http://sds.podval.org/) on darwin Ns 10.3.1504 http://steingoldpsychology.com http://www.childpsy.net http://thereligionofpeace.com https://jihadwatch.org http://memri.org My plan for 'after work' is to retire. |
From: <Joe...@t-...> - 2017-12-20 18:16:18
|
Hi again, Having fun, here's the version with the need for most gensyms removed: (defmacro lazy-let1 ((var) &body body) (let ((shadow (gensym (symbol-name var)))) `(let ((,shadow 'unbound)) (flet ((,shadow () (if (eq ,shadow 'unbound) ,var ,shadow)) ((setf ,shadow) (,var) (setf ,shadow ,var))) (symbol-macrolet ((,var (,shadow))) .,body))))) (setf ,shadow) is indeed defined as (setf ,shadow) - scoping rules! 'unbound should be replaced by the usual unique CONS trick, and perhaps #1#... This is just for the fun, I don't like this macro because of the run-time check. Actually, it has huge costs in clisp - try out DISASSEMBLE - as the closures are not inlined. Regards, Jörg |
From: Sam S. <sd...@gn...> - 2017-12-20 19:04:35
|
> * <Wbret-Plevy.Ubruyr@g-flfgrzf.pbz> [2017-12-20 18:16:08 +0000]: > > Having fun, here's the version with the need for most gensyms removed: > > (defmacro lazy-let1 ((var) &body body) > (let ((shadow (gensym (symbol-name var)))) > `(let ((,shadow 'unbound)) > (flet ((,shadow () (if (eq ,shadow 'unbound) ,var ,shadow)) > ((setf ,shadow) (,var) (setf ,shadow ,var))) > (symbol-macrolet ((,var (,shadow))) .,body))))) > > (setf ,shadow) is indeed defined as (setf ,shadow) - scoping rules! > > 'unbound should be replaced by the usual unique CONS trick, and perhaps #1#... > > This is just for the fun, I don't like this macro because of the run-time check. This is a proof of concept. If we can fix all the LOOP bugs (search for #-CLISP in loop.tst) using lazy-let, it would lend support to my TAGLET proposal: (taglet (vars) &body) == (lazy-let (vars) (tagbody ,@body)) TAGLET fits the way LOOP is generally _perceived_ very well. > Actually, it has huge costs in clisp - try out DISASSEMBLE - as the > closures are not inlined. Inlining the flet closures has been in clisp man page TODO section as long as I can remember. ;-) Thanks. -- Sam Steingold (http://sds.podval.org/) on darwin Ns 10.3.1504 http://steingoldpsychology.com http://www.childpsy.net https://jihadwatch.org http://mideasttruth.com http://americancensorship.org http://memri.org When told to go to hell, ask for directions. |
From: <Joe...@t-...> - 2017-12-21 16:45:25
|
Sam, Your lazy-let reminds me of the trouble Pythonistas have with the local/global variable dichotomy and the `global` statement. That made it into the Python programming FAQ. IIRC, setting a variable first defines it in the local environment; only reading one sees the global environment. https://docs.python.org/3/faq/programming.html#what-are-the-rules-for-local-and-global-variables-in-python I believe the Python lesson clearly is to *not* re-invent that trouble in Lisp. Regards, Jörg |
From: Sam S. <sd...@gn...> - 2017-12-21 17:15:00
|
Jörg, > * <Wbret-Plevy.Ubruyr@g-flfgrzf.pbz> [2017-12-21 16:45:16 +0000]: > > Your lazy-let reminds me of the trouble Pythonistas have with the > local/global variable dichotomy and the `global` statement. That made > it into the Python programming FAQ. IIRC, setting a variable first > defines it in the local environment; only reading one sees the global > environment. > https://docs.python.org/3/faq/programming.html#what-are-the-rules-for-local-and-global-variables-in-python No, this is very different. The Python global/local problem is because the way a variable (first) _used_ affects how it works. My LAZY-LET merely separates initialization from binding. I.e., I allow code between binding and initialization to ignore the binding. The idea is to make the macroexpansion of loop behave the way people perceive loop to operate. (macroexpand '(loop ....)) looks like this: --8<---------------cut here---------------start------------->8--- (MACROLET ((LOOP-FINISH NIL (SYSTEM::LOOP-FINISH-ERROR))) (BLOCK NIL (LET ((...)) (PROGN (LET ((...)) (LET NIL (LET ((...)) (LET NIL (MACROLET ((LOOP-FINISH NIL '(GO SYSTEM::END-LOOP))) (TAGBODY (SETQ ...) SYSTEM::BEGIN-LOOP ....)))))))))) --8<---------------cut here---------------end--------------->8--- most of those LET outside of TAGBODY are _not_ supposed to initialize the variables they bind. > I believe the Python lesson clearly is to *not* re-invent that trouble > in Lisp. TAGLET is _not_ supposed to be a public interface, just an internal tool to implement LOOP. It closely corresponds to how people read LOOP, so it has the best chance to result in code doing what the user expects. -- Sam Steingold (http://sds.podval.org/) on darwin Ns 10.3.1504 http://steingoldpsychology.com http://www.childpsy.net http://www.dhimmitude.org http://thereligionofpeace.com http://honestreporting.com http://jij.org Whom computers would destroy, they must first drive mad. |
From: Bruno H. <br...@cl...> - 2017-12-23 21:38:27
|
Jörg wrote: > I've not yet attacked the problem of iteration in LOOP. I'm afraid there will be no way to > make everybody happen w.r.t. the value of variables in FINALLY clauses. There's already an issue write-up at http://cliki.net/Issue%20LOOP-FINALLY-VARIABLES . Feel free to add write-ups for the other issues in LOOP ! Bruno |