I need to do a bit of Parenscript hacking for my project and thought I'd upgrade to the latest source first. I had to roll back to the released version, though, because of problems with how code is being generated for let.
First, the semantics of let have changed to be more Lisp-like. Is that correct? This causes some practical problems. In much of our app, performance is critical and we can't afford to have temporary variable reassignment, try/finally, and delete magic going on under the hood. What we need is plain-old, naked Javascript variable assignment - the simplest generated code possible. As of 20071104, that's exactly what PS generated for let. Now, it generates code that's considerably more complex, which we mostly can't use.
Second, there's the philosophical question (debated on this list several times) about how Lispy Parenscript should try to be. There are good arguments either way, but it seems to me important to remember that Parenscript is not a CL-to-JS compiler, it's JS (plus macros) embedded in CL. The bias so far, which I agree with, has been "when in doubt, adhere closely to JS semantics", because we need the generated JS to be fast, readable, and easy to debug. That's a big deal if you spend a lot of time working with the JS in Firebug (which we do). It seems to me that a CL-to-JS compiler is impractical unless two conditions are fulfilled: the standard JS interpreters are performant enough to handle it, and we no longer have to work manually with the generated code.
My questions are: (1) Does anyone need Lispy let, or can we just take it out and revert to the simpler let? (2) If it really is needed, can we come up with an easy way to offer the user their choice of assignment semantics (perhaps a special variable)?
In the meantime, I'll switch back to 20071104.
Daniel
I have only limited input here.
On Sat, Aug 9, 2008 at 1:23 PM, Daniel Gackle danielgackle@gmail.com wrote:
My questions are: [...] (2) If it really is needed, can we come up with an easy way to offer the user their choice of assignment semantics (perhaps a special variable)?
I would much prefer two different macros in order not to cause issues with a special variable when working with Parenscript from different sources.
Daniel
Red
< two different macros in order not to cause issues with a special variable when working with Parenscript from different sources >
It sounds like you're talking about something important, but I'd need to see an example in order to get it. However, I'd like an answer to the first question first - does anybody actually need this? - because if not, then maybe the original let can be restored (either by removing or renaming the new one).
Another point: the new let generates different code for variables defined with "defvar" than "var". If the intention is to create Lispy bindings for special variables (indicated by "defvar") then an alternative solution would be to generate fancy code only for variables declared this way, and return to generating plain-old-JS for variables not declared this way. That might be a simple way to get the best of both worlds.
Daniel
On Sat, Aug 9, 2008 at 4:29 PM, Red Daly reddaly@gmail.com wrote:
I have only limited input here.
On Sat, Aug 9, 2008 at 1:23 PM, Daniel Gackle danielgackle@gmail.com wrote:
My questions are: [...] (2) If it really is needed, can we come up with an easy way to offer the user their choice of assignment semantics (perhaps a special variable)?
I would much prefer two different macros in order not to cause issues with a special variable when working with Parenscript from different sources.
Daniel
Red _______________________________________________ parenscript-devel mailing list parenscript-devel@common-lisp.net http://common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel
Daniel Gackle wrote:
First, the semantics of let have changed to be more Lisp-like. Is that correct? This causes some practical problems. In much of our app, performance is critical and we can't afford to have temporary variable reassignment, try/finally, and delete magic going on under the hood. What we need is plain-old, naked Javascript variable assignment - the simplest generated code possible. As of 20071104, that's exactly what PS generated for let. Now, it generates code that's considerably more complex, which we mostly can't use.
My questions are: (1) Does anyone need Lispy let, or can we just take it out and revert to the simpler let? (2) If it really is needed, can we come up with an easy way to offer the user their choice of assignment semantics (perhaps a special variable)?
The semantics of LET (and DO) did change to be more lisp-like. At the same time, LET* and DO* were introduced for access to more native JS semantics.
The idea is that PS feels pretty close to lisp in other ways, so the expected semantics of CL:LET should be broken as little as possible when there already exists a CL construct that better matches native JS semantics: LET*. When writing PS, I use LET* most of the time for the same reasons you outlined above. I still prefer having that * there as a visual reminder of what is really going on.
Is this change still going to be a huge issue for you now that you know we introduced LET*? It seems that if you want LET*-like semantics everywhere in your existing code, you just need to search/replace all of your LETs into LET*s (or, alternatively, locally bind PS:LET to PS:LET*).
Cheers,
-- Travis
What you describe would be a good compromise, and I'm fine with using let* instead of let. However, let* doesn't work as you describe in the current darcs version of PS:
(ps (defun blah () (let* ((x 3)) (return x))))
=>
"function blah() { var tempstackvar26357; try { tempstackvar26357 = x; x = 3; return x; } finally { x = tempstackvar26357; delete tempstackvar26357; }; };"
What I need here, of course, is:
"function blah() { var x = 3; return x; };"
Is the latter what you intend let* to produce? If so, that would work for me.
By the way, I'm not opposed to adding new abstractions to PS - there are a lot of great things that can be added. A good example is the work Red did to support &optional and &key parameters. That was a nice hack because the JS semantics (i.e. its arguments array) are close enough to Lisp to bridge the gap. (Even so, there are a few functions where I don't want to pay the runtime price for that abstraction).
Another promising area might be CL's sequence functions. I bet these would map fairly well to JS arrays.
On the other hand, the world of cons/car/cdr doesn't map well. I'm dealing with this right now because we have a sizeable chunk of CL code that also needs to run in the browser. I'm hacking a rudimentary compiler to generate JS for it (basically a bunch of local ps-macros to coax PS into compiling the CL code). What I don't want to do is implement Lisp conses in JS. That's an area, I think, where the gap is too huge to be bridged well.
Dan
On Sun, Aug 17, 2008 at 4:26 AM, Travis Cross tc@travislists.com wrote:
Daniel Gackle wrote:
First, the semantics of let have changed to be more Lisp-like. Is that correct? This causes some practical problems. In much of our app, performance is critical and we can't afford to have temporary variable reassignment, try/finally, and delete magic going on under the hood. What we need is plain-old, naked Javascript variable assignment - the simplest generated code possible. As of 20071104, that's exactly what PS generated for let. Now, it generates code that's considerably more complex, which we mostly can't use.
My questions are: (1) Does anyone need Lispy let, or can we just take it out and revert to the simpler let? (2) If it really is needed, can we come up with an easy way to offer the user their choice of assignment semantics (perhaps a special variable)?
The semantics of LET (and DO) did change to be more lisp-like. At the same time, LET* and DO* were introduced for access to more native JS semantics.
The idea is that PS feels pretty close to lisp in other ways, so the expected semantics of CL:LET should be broken as little as possible when there already exists a CL construct that better matches native JS semantics: LET*. When writing PS, I use LET* most of the time for the same reasons you outlined above. I still prefer having that * there as a visual reminder of what is really going on.
Is this change still going to be a huge issue for you now that you know we introduced LET*? It seems that if you want LET*-like semantics everywhere in your existing code, you just need to search/replace all of your LETs into LET*s (or, alternatively, locally bind PS:LET to PS:LET*).
Cheers,
-- Travis
Daniel Gackle wrote:
What you describe would be a good compromise, and I'm fine with using let* instead of let. However, let* doesn't work as you describe in the current darcs version of PS:
(ps (defun blah () (let* ((x 3)) (return x))))
=> [snip...]
You must have previously declared X to be special, as with ps:defvar. Without such a declaration, I get:
function blah() { var x = 3; return x; };
(check your ps::*ps-special-variables*)
As for the handling of specials, what can I say? I didn't add the code that does this, but it appears that the goal was to preserve semantics. It actually seems to do so most of the time (even in the case above, where one might incorrectly assume that the return statement would short-circuit the finally clause).
I'm not certain that the current implementation is quite right though. I picture PS as a compiler, and a compiler shouldn't keep persistent global state. What happens when I want to compile two independent JS files in the same lisp image? Clearly the specials declared in one should not necessarily carry over to the other, as they do currently.
The next time I dive in to make major changes to Parenscript, I plan to find a way to address this coherently, such as by encapsulating the compile state into an object or closure (patches accepted in the meantime, of course). There are valid reasons to want your compile state to be persistent (building JS code on demand to send to the browser during an AJAX session comes to mind).
At any rate, you should be able to avoid the funky try/finally code by either not declaring X special, or (if you otherwise want X to be special), writing:
(ps (defun blah () (var x 3) (return x)))
which, semantically, is the closest to what you really want anyway.
Cheers,
-- Travis
As for the handling of specials, what can I say? I didn't add the code that does this, but it appears that the goal was to preserve semantics. It actually seems to do so most of the time (even in the case above, where one might incorrectly assume that the return statement would short-circuit the finally clause).
That was my intention when adding specials to PS. I strongly suspect there are situations where the semantics differ, but it's good enough for most common cases without being unduly complex.
Overall, with specials and let vs let* being examples, I'd like Parenscript code to follow the usual-case Common Lisp semantics to enable sharing code between the two. This would make the scenario that Daniel is describing where you'd want the same code to run on the server and browser a lot less painful. The caveat to this is that the goal of translation simplicity/generated JS readability/debugability trumps the former consideration when it comes time to make trade-offs in how closely CL features are implemented in the generated JS. It all comes down to making a reasonable compromise, where reasonable is defined as "is this good/fast enough for the web apps I'm working on right now?"
The other pitfall is that any differences in semantics between the two will inevitably cause hard-to-find bugs when the code depends on the more obscure behavior. That is something I am personally willing to live with right now, but it's obviously not the right thing.
I'm not certain that the current implementation is quite right though. I picture PS as a compiler, and a compiler shouldn't keep persistent global state. What happens when I want to compile two independent JS files in the same lisp image? Clearly the specials declared in one should not necessarily carry over to the other, as they do currently.
The next time I dive in to make major changes to Parenscript, I plan to find a way to address this coherently, such as by encapsulating the compile state into an object or closure (patches accepted in the meantime, of course). There are valid reasons to want your compile state to be persistent (building JS code on demand to send to the browser during an AJAX session comes to mind).
I agree and I'd like to see your design.
Thanks, Vladimir
At any rate, you should be able to avoid the funky try/finally code by either not declaring X special, or (if you otherwise want X to be special), writing:
(ps (defun blah () (var x 3) (return x)))
which, semantically, is the closest to what you really want anyway.
Cheers,
-- Travis _______________________________________________ parenscript-devel mailing list parenscript-devel@common-lisp.net http://common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel
On Sat, Aug 9, 2008 at 10:23 PM, Daniel Gackle danielgackle@gmail.com wrote:
What we need is plain-old, naked Javascript variable assignment - the simplest generated code possible. As of 20071104, that's exactly what PS generated for let. Now, it generates code that's considerably more complex, which we mostly can't use.
When parenscript changed maintainer there was a discussion about how parenscript should be improved, in the Lisp direction or stay as a simple translator. I was affraid of things like these, so I have kept using the old repo which is at common-lisp.net/project/ucw/public_html/repos/parenscript. It think it is very stable and it works good if you want a plain translator.
/Henrik Hjelte
parenscript-devel@common-lisp.net