I tried these out, and they don't seem to help, at least in Chrome:
var x = 1;
{
var x = 2;
var y = "but why!?";
alert( x ); // 2
}
alert( x ); // 2
alert( y ); // but why!?
var x = 1;
do
{
var x = 2;
var y = "but why!?";
alert( x ); // 2
}
while( false );
alert( x ); // 2
alert( y ); // but why!?
The next thing you might try is mangling variable names. If you expect the JavaScript code to never use threads (which it doesn't, I think), you could temporarily replace the variable values using assignment.
For a high-level (and non-idiomatic) approach, it's possible to implement trampolining using exceptions. I've done it in about a page of JavaScript (right next to my Fermat's Last Theorem proof, of course of course :-p ), and I think the API can be as simple as this:
- Use bounce( foo, foo.bar, 1, 2 ,3 ) instead of
foo.bar( 1, 2, 3 ) in tail positions. This throws an
exception containing information about the next call
to make.
- To begin a trampolining loop, call the function with
trampoline( foo, foo.bar, 1, 2, 3 ). This should be
done when calling functions at the top level and when
calling functions from within functions that are to be
exposed to other JavaScript utilities and DOM event
handlers (which won't call trampoline themselves).
Okay, so determining when a closure could "escape" into the outside world might be hard. O_o To avoid that trouble, uh, keep a global variable saying that a trampolining loop is already in progress, and use trampoline( ... ) for almost every single call? Well, that'll complicate things a little. ^_^;
> The next thing you might try is mangling variable names.
I think this could work pretty nicely, actually:
(let x 5 var _x;
... ) _x = 5;
...
Here's an example with a global and local variable:
(= x 6) var x, _x;
(let x 5 x = 6;
(alert x))
(alert x) _x = 5;
alert(_x); // => 5
alert(x); // => 6
If you use multiple lets with the same variable name in one scope, you would need to differentiate with additional underscores or something:
(let x 5 var _x, __x;
(alert x))
(let x 6 _x = 5;
(alert x)) alert(_x); // => 5
__x = 6;
alert(__x); // => 6
Of course, there is still some risk of variable name collision, and the output isn't super readable. Maybe it's good to have two operators, `let` and `let!`. The former uses function scope, so it's safer and compiles to more readable JavaScript, but it increases the stack size. The latter (let!) is the optimized version that uses one of the schemes we've been talking about.
> What's your policy regarding the ability for people to say (let _x 5 ...) to spoof the mangler? ;)
The underscore prefix system would be nice because it's pretty readable, but it's not necessary. In the worst case, we can use gensyms. I thought using underscores for internal variables might work because CoffeeScript does it [1], but maybe it won't. So you raise a good point. :)
> Also, will the name "let!" conflict with ssyntax?
Yes. I'm starting from scratch with the ssyntax. `!` is a regular character now. It can be used in names like `let!`, and when by itself in functional position it compiles to the JavaScript negation operator:
(! x) => !x
`.`, `:`, `~`, `[]`, `&` and friends are out, too. This may displease my Arc buddies, but I just found it too difficult to think in JavaScript when the gap in meaning between Arc and JS for these characters was so large. `.` compiles to the JS dot operator now, `{}` to the JS object literal and `[]` to the array literal (see [2] for some details). Note that quote ('), quasiquote (`), unquote (,), unquote-splicing (,@) and string quoting (") should all work as expected.