Below is a ps macro that simulates the shadowing of special variables in Lisp. I'm wondering if anyone thinks this would be useful to add to Parenscript.
I wrote it because I have some Javascript functions that reference global variables, and wanted to write some test functions for those without modifying global state. One option of course would be to simply write the original functions to take parameters instead of the global variables. But that complicates their signatures and I'm loath to modify production code to suit tests. With this macro, my test can do this:
(ps (shadow-let ((*global-var* "test value")) (function-that-uses-global-var)))
and the original value of *global-var* will be restored:
var _js3778 = null; try { _js3778 = GLOBALVAR; GLOBALVAR = 'test value'; functionThatUsesGlobalVar(); } finally { GLOBALVAR = _js3778; };
Daniel
------------------------------------------------------------------------
(defpsmacro shadow-let (bindings &body body) (labels ((wrap-expr (bindings body) (if (null bindings) body (list (list 'temporarily-bind (car bindings) (wrap-expr (cdr bindings) body)))))) `(macrolet ((temporarily-bind ((var expr) body) (with-ps-gensyms (temp) `(progn (defvar ,temp nil) (try (progn (setf ,temp ,var) (setf ,var ,expr) ,@body) (:finally (setf ,var ,temp))))))) ,(cons 'progn (wrap-expr bindings body)))))
Instead of introducing a new special form or macro, I decided to follow Common Lisp and make defvar define top-level special forms, which are automatically dynamically bound by let. Now you need to use the special form 'var' if you just want to define regular globals. This, and a few other minor things are now in the darcs repository (which should have sent a message to this group after my push, but I guess I didn't configure it right, yet).
Merry X-mas, Vladimir
On 11/6/07, Daniel Gackle danielgackle@gmail.com wrote:
Below is a ps macro that simulates the shadowing of special variables in Lisp. I'm wondering if anyone thinks this would be useful to add to Parenscript.
I wrote it because I have some Javascript functions that reference global variables, and wanted to write some test functions for those without modifying global state. One option of course would be to simply write the original functions to take parameters instead of the global variables. But that complicates their signatures and I'm loath to modify production code to suit tests. With this macro, my test can do this:
(ps (shadow-let ((*global-var* "test value")) (function-that-uses-global-var)))
and the original value of *global-var* will be restored:
var _js3778 = null; try { _js3778 = GLOBALVAR; GLOBALVAR = 'test value'; functionThatUsesGlobalVar(); } finally { GLOBALVAR = _js3778; };
Daniel
(defpsmacro shadow-let (bindings &body body) (labels ((wrap-expr (bindings body) (if (null bindings) body (list (list 'temporarily-bind (car bindings) (wrap-expr (cdr bindings) body)))))) `(macrolet ((temporarily-bind ((var expr) body) (with-ps-gensyms (temp) `(progn (defvar ,temp nil) (try (progn (setf ,temp ,var) (setf ,var ,expr) ,@body) (:finally (setf ,var ,temp))))))) ,(cons 'progn (wrap-expr bindings body))))) _______________________________________________ parenscript-devel mailing list parenscript-devel@common-lisp.net http://common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel
A few months ago, Vladimir posted an email along with a rather dramatic new feature in Parenscript: the ability to dynamically rebind vars within a scope and have their original value restored on leaving the scope; in other words, Lisp-style special variables. As part of this change, "defvar" was redefined to declare this new kind of variable (invoking quite different behavior by the PS compiler under the hood). If you want old-fashioned JS variables you now need to say "var" instead. I both like and dislike this.
What I like: the feature itself. One of the best things about Common Lisp is that we get the advantages of global variables without the pain. I had even written a ps macro to do something similar in a few cases. But those cases are rare. The vast majority of the time I *don't *want this behavior; I want plain JS variables. This is because I need runtime performance, ease of debugging, and and so on.
I also like the idea of ordinary JS variables being declared using "var", not "defvar". That's because "var" reminds me that I'm dealing with JS scoping rules which are so different from Lisp's. It's also shorter.
What I dislike: the use of the term "defvar" to declare the new type of variable. I'm skeptical for three reasons:
1. The word "defvar" is very similar to "var", whereas the behavior of the two is very different; 2. "defvar" has a specific meaning in the Lisp world and it's not what PS is doing here. This is misleading. PS is not Lisp, and we shouldn't establish nominal equivalences where there isn't a semantic equivalence (or at least a close correspondence). 3. Lots of existing PS code says "defvar" to mean "var", all of which will now break. Actually, it's worse. It breaks in a few strange places, while silently compiling (but generating considerably more complex JS) most of the time.
For these reasons I'm thinking we should keep the feature, but not use "defvar" for it. I'd suggest either "define-special-variable" or "defparameter". The former is verbose but explicit. The latter is more Lispy, addresses #1 and #3 completely and does a better job on #2. I guess I have a slight preference for "defparameter" for the somewhat embarrassing reason that it will be syntax highlighted in Emacs.
This would then free up the term "defvar", allowing PS to apply the same policy here that was used in previous cases of name changes: keep the behavior the same as it was, but issue a "deprecated" warning specifying what the new term should be.
Opinions?
Daniel
On Mon, Dec 24, 2007 at 7:25 PM, Vladimir Sedach vsedach@gmail.com wrote:
Instead of introducing a new special form or macro, I decided to follow Common Lisp and make defvar define top-level special forms, which are automatically dynamically bound by let. Now you need to use the special form 'var' if you just want to define regular globals. This, and a few other minor things are now in the darcs repository (which should have sent a message to this group after my push, but I guess I didn't configure it right, yet).
Merry X-mas, Vladimir
On 11/6/07, Daniel Gackle danielgackle@gmail.com wrote:
Below is a ps macro that simulates the shadowing of special variables in Lisp. I'm wondering if anyone thinks this would be useful to add to Parenscript.
I wrote it because I have some Javascript functions that reference
global
variables, and wanted to write some test functions for those without modifying global state. One option of course would be to simply write
the
original functions to take parameters instead of the global variables.
But
that complicates their signatures and I'm loath to modify production
code to
suit tests. With this macro, my test can do this:
(ps (shadow-let ((*global-var* "test value")) (function-that-uses-global-var)))
and the original value of *global-var* will be restored:
var _js3778 = null; try { _js3778 = GLOBALVAR; GLOBALVAR = 'test value'; functionThatUsesGlobalVar(); } finally { GLOBALVAR = _js3778; };
Daniel
(defpsmacro shadow-let (bindings &body body) (labels ((wrap-expr (bindings body) (if (null bindings) body (list (list 'temporarily-bind (car bindings) (wrap-expr (cdr
bindings)
body)))))) `(macrolet ((temporarily-bind ((var expr) body) (with-ps-gensyms (temp) `(progn (defvar ,temp nil) (try (progn (setf ,temp ,var) (setf ,var ,expr) ,@body) (:finally (setf ,var ,temp))))))) ,(cons 'progn (wrap-expr bindings body))))) _______________________________________________ parenscript-devel mailing list parenscript-devel@common-lisp.net http://common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel
parenscript-devel mailing list parenscript-devel@common-lisp.net http://common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel
parenscript-devel@common-lisp.net