The concern is that because Coffeescript automatically scopes variables to the scope of their first reference, it can introduce maintenance issues. Consider the following:
foo = ->
bar = "woot!"
console.log bar
This compiles to:
var foo;
foo = function() {
var bar;
bar = "woot!";
return console.log(bar);
};
bar is locally scoped to foo(). Now, 2 weeks later and 200 lines earlier, you come along and define:
bar = ->
alert "Holy crap cheese is awesome!"
Which compiles to:
var bar, foo;
bar = function() {
return alert("Holy crap cheese is awesome!");
};
foo = function() {
bar = "woot!";
return console.log(bar);
};
Now, all of a sudden, the "bar" reference in foo isn't scoped to foo() anymore, it's scoped globally, and once you invoke foo(), it'll replace the function bar with a string, potentially breaking your app. It's an ease-of-maintenance issue.
This isn't consistent behavior, though. If you define your top-level bar() function after foo, like so:
foo = ->
bar = "woot!"
console.log bar
bar = ->
alert "Holy crap cheese is awesome!"
Then you get "correct" scoping (and the outer bar is shadowed):
var bar, foo;
foo = function() {
var bar;
bar = "woot!";
return console.log(bar);
};
bar = function() {
return alert("Holy crap cheese is awesome!");
};
On one hand, it could be argued that this is a "name things better" problem, but on the other, I have to agree that it'd be nice to be able to explicitly scope things when needed. Given that the behaviors are divergent based on what order the variables appear in, I'd say it's confusing enough that a way to explicitly say "hey, I know what I'm doing, I want to shadow any outer variables and declare local scope here" would be useful.
The inconsistency doesn't stop there; `for` variables are specialcased:
bar = ->
alert "Holy crap cheese is awesome!"
foo = ->
for bar of bars
console.log bar
return
↓
var bar, foo;
bar = function() {
return alert("Holy crap cheese is awesome!");
};
foo = function() {
var bar;
for (bar in bars) {
console.log(bar);
}
};
You do want to sometimes reference outer scope variables from inner scopes, though. The only difference between a leaked local and a properly referenced global is usage semantics.
There is some discussion here on why a "bar" variable at top level only affects the scoping of other "bar" variables that are lexically below "bar" in the file, not above it.
No. That's the crux of the complaint. In plain Javascript, you can use "var x" to scope x to the current scope (and in 1.7, you can use 'let' to scope to the current block, not just to a function), but there's no corollary in Coffeescript, since Coffeescript manages scoping for you automagically. It's a convenience 99% of the time, but the 1% of the time that it's not, it's a giant pain in the ass. If there were something like var/let/local, you could solve the problem manually, but as the Twitter exchange indicated, there is no plan to add it.
The solution/workaround is "use descriptive names and avoid polluting scopes", but the reality of software development is that you're eventually going to cross those wires, and it's going to make you crazy until you figure out what happened.