That's a whole different matter, which can't make you shoot yourself in the foot without knowing it for any non-trivial code.
CoffeeScript's scoping forces you to always keep track of whatever is enclosing the current scope ALL THE WAY TO THE TOP. This is way too much when your function doesn't need to access outer variables (which should be the minority of the cases).
So, problem is, you either make all your functions have non-free variables (but Jashkenas seems to dislike functions shadowing outer variables too, which is just... overtly weird), or you keep track of all variables above the current scope.
The former is not too unreasonable, until you remember it makes no sense with closures :3
I actually agree with his stance against shadowing variables, just on philosophical grounds. It encourages good, descriptive naming. On the other hand, I acknowledge that sometimes it's desirable to shadow outer variables. I think it should be discouraged, but not prevented.
I think the big problem with Coffeescript's behavior is that it can introduce some damn subtle bugs that can be really hard to track down if you don't know what you're looking for, because you're not able to explicitly specify scope semantics. It's even worse if you're polluting higher-scope variables of the same type, because it becomes even less obvious where the error comes from.
Coffeescript more or less shares Ruby's scoping rules, but there's a cultural difference between the Ruby and Javascript communities that makes it a little less workable in Javascript. Specifically, Ruby's "everything is an object", aggressive use of namespacing, and the general idiom that only constants go into the global namespace tends to limit scope issues that could arise from mix-ins.
Coffeescript does attempt to mimic this by providing class semantics and wrapping everything in anonymous functions to limit scope leak, but there's still a lot of temptation to just create a bunch of top-level functions, and that leads to situations like the one described in the blog post.
The problem is actually worse. You also have to keep track of all the variables BELOW the current scope as well so you don't accidentally (as in the case of the article) turn a declaration into a reassignment.
You are correct that variable scoping goes all the way to the top, but nobody is forcing you to create top-level variables with overloaded names like "log". Seriously, if you have a file that uses log files and logarithms, just take some care to distinguish the concepts. Use "log_file" for log files; use "logarithm" or "Math.log" for inverse exponentation.
It's a serious suggestion. Instead of introducing four top-level variables (log, sin, cos, and tan), just introduce one (Math) that wouldn't possibly conflict with a local.
You are correct about "last", "start's, etc., but there are also good examples in the CS sourcecode of easily avoiding naming conflicts with a sensible naming convention:
And where would JavaScript not handle them in a way people don't expect them to be handled?
Comparing Python's handling of semicolons with JavaScript's ASI, you get the following:
-- End of statements
Python and JavaScript: Ends with either a new line or a semicolon.
-- Line joining
Python: whitespace strict, so lines must be either explicitly joined through the `\' escape character, or put inside a parenthesized expression, which ignores whitespace.
JavaScript: non-whitespace strict, so lines don't need to be explicitly joined. Statements may be continued by starting the following line with a continuation token (one of: `[ ( + - /')
-- Exceptions
Python: Parenthesized expressions use a different parser state (ie.: whitespace strict vs non-whitespace strict)
JavaScript: restricted production grammar usually have optional expressions following them, and since they are a valid statement on their own, the statement will be ended with a new line. So, the related information must start in the same line.
Restricted productions are: return, break and continue; Post-fix and prefix operators are included as restricted productions for readability's sake.
I fail to see how that's difficult to remember, or how that's insanely more complex than Python's rules...
I really don't see any problem with the way JavaScript handles it. "Unless the following line can be part of the preceding statement, end the current statement" is a pretty natural thing to me. But well, I guess that's a matter of taste, and you can't really discuss that...
That's not a problem with the line joining stuff. Both `return' and `{ foo: bar }' are ENTIRE VALID statements in their own right.
returnStmt ::= "return" [ expression ] (";" or EOL)
Note that expression is optional, so it's the choice of `return' by itself being a valid statement on its own that "causes problems".
Plus, this is not really a problem that can be solved by simply putting semicolons everywhere. You can't change the parser rules by including or not semicolons.
Also, same thing with break/continue:
breakStmt ::= "break" [ label ] (";" or EOL)
continueStmt ::= "continue" [ label ] (";" or EOL)
`return` may be a valid statement but an object literal is not.
A standalone object literal would likely be interpreted as a block and would throw a syntax error unless it were a simple identifier and a statement `{ foo: <statement> }`, in which case it would not interpret it as an object but exactly what it is, a label and a statement.
This is why JSON parsers which use eval must first wrap the object literal with parens, to make it an expression rather than a statement.
Never said it was an object literal eh. Object literals can only occur inside expressions, that's pretty well defined in the JavaScript AST too, which is described to the utmost detail in the ECMAScript specs.
The thing is:
- `return` alone is a valid statement in its own.
- The parser just need to use a simple lookahead to decide whether the next line is part of the previous statement.
- The parser DON'T change the meaning of your programs.
All that's beside that is a matter of taste. Whether use semicolons or not is up to what sits better with each person. I'm just arguing against the silly and incorrect "technical" arguments against it.
CoffeeScript's scoping forces you to always keep track of whatever is enclosing the current scope ALL THE WAY TO THE TOP. This is way too much when your function doesn't need to access outer variables (which should be the minority of the cases).
So, problem is, you either make all your functions have non-free variables (but Jashkenas seems to dislike functions shadowing outer variables too, which is just... overtly weird), or you keep track of all variables above the current scope.
The former is not too unreasonable, until you remember it makes no sense with closures :3