Actually, you don't understand lexical scope
github.com
github.com
The pitch: "CoffeeScript is a better syntax for writing JavaScript."
The reality: "CoffeeScript does not behave like JavaScript in subtle, non-syntactical ways."
It is often suggested that people use CoffeeScript instead of JavaScript. But this understanding of CoffeeScript is inaccurate. CoffeeScript has different semantics from JavaScript, and the obvious expansion of CoffeeScript back into JavaScript is a wrong expansion. A knowledgeable JavaScript developer will make mistakes when making simple changes to CoffeeScript code that they did not write. They will have difficulty fixing bugs in CoffeeScript code.
If you like, you can be a CoffeeScript shop. Find senior developers who use CoffeeScript and teach junior developers to use it. Code will be a little easier to write, and, like nerf guns and foosball, you might seem like a superficially cool place to work. Like any new technique, you'll be out on your own. You'll run into bugs, you'll deal with toolchain support, you'll find optimization issues.
There are reasons to translate other languages into JavaScript. Macros, static typing, execution in other contexts. CoffeeScript gives you ruby-like syntax... To me, that just doesn't seem worth it.
Nothing in the post supports this assertion. You may have your own reasons for feeling this way, but the difference between JavaScript and CoffeeScript with respect to the "var" keyword is neither subtle nor obvious, given that CoffeeScript does not have a var keyword.
The only possible source of literal misinterpretation would be to look at a CoffeeScript file and think that every variable that wan't a parameter was a global variable. This is extremely unlikely in practice.
I read you as being biased against using it. FIne with me, Chief, go your way in peace. But don't try to twist my example into support for a decision you have already made.
Great, then every variable in CoffeeScript must be global, because that's the semantics of a non-parameter not be declared a "var" in JavaScript. Except that this is not an accurate translation, as the entire post goes on to explain scoping in CoffeeScript.
The only possible source of literal misinterpretation would be to look at a CoffeeScript file and think that every variable that wan't a parameter was a global variable. This is extremely unlikely in practice.
Screwing up scoping is an extremely common JavaScript mistake. If you showed me JavaScript-like code and told me there was a bug, the first thing I would point out is that you declared everything globally. Oh, that isn't the case in CoffeeScript? Huh, I guess you're on your own then, because I didn't learn your goofy alternative JavaScript syntax. Maybe you think the junior kid who has a bug in his code is going to properly explain the subtlety of CoffeeScript scoping.
FIne with me, Chief, go your way in peace. But don't try to twist my example into support for a decision you have already made.
It's offensive of you to dismiss my criticism in this forum. I am not prejudiced against new things, and while CoffeeScript doesn't seem like a worthwhile idea to me, it's community is far more offensive. CoffeeScript's demagoguery of JavaScript might impress some folks, but outside ruby-like syntax, CoffeeScript doesn't solve any fundamental issues.
So, "go your way in peace", and leave me to my thoughts, please.
It is a fact that it is possible to write CoffeeScript code that requires careful examination of the file to understand what it does, code which can also change if you alter something somewhere else in the file.
My opinion--which I also believe I have demonstrated to be a fact--is that it is possible to write sane JavaScript that is lexically scoped and not subject to misinterpretation.
It is clearly possible to write JavaScript code that requires careful examination of the file to understand what it does, acode which can also change if you alter something somewhere else in the file.
I have zero quibble with you or anyone else feeling CoffeeScript doesn't solve a problem for them. I have zero quibble with you or anyone else saying "Notwithstanding the fact that CoffeeScript is a lexically scoped language, my opinion is that I hate it."
My only quibble--and it is minor--is that it is not possible to say that CoffeeScript function-local variables don't work like JavaScript function-local variables and that you can confuse the two, since without the var keyword nobody can look at a CoffeeScript file and think they are looking at function-local variables.
We both agree on the fact that a file with variables that are not parameters in CoffeeScript has different semantics from a file with variables that are neither parameters nor function-local in JavaScript.
If your opinion is that this is a fatal flaw, well, who am I to argue with the fact that this is your opinion? I have a different opinion, and we are both entitled to same provided neither of us claim it to be a fact.
You can take the rest of my original comment as a rehashing of my evaluation of CoffeeScript, since I only see it when it appers on Hacker News.
Pure profit for me, but as always YMMV.
Possible is an extremely low bar. We hold ourselves to a higher standard these days in computer science, including language design.
It is possible to write sane INTERCAL that is (however INTERCAL is scoped) and not subject to misinterpretation.
It does point out that it has function-level lexical scope and that it has a construct for creating block scope which is almost identical to "let" as found in Scheme.
You can decide for yourself whether this is an advance on JavaScript or a retreat to the 1970s. If I had to defend the entire language every time I discussed CoffeeScript, mys posts would contain even more verbiage and repetition than they already do.
The thing that is subtle and nonobvious as a javascript programmer is the way CoffeeScript variables are scoped. They are not scoped like "var" variables in javascript. They are not scoped like "non-var" variables in javascript either. They have their own kind of scope that is unique to CoffeeScript.
Now you can argue that the way javascript scopes variables is wrong, and I'd agree with you. You can argue that the CoffeeScript way is better, and that's fine. But it makes it a different language from JavaScript, and it has all the baggage that goes along with that. You can't just treat CoffeeScript as "javascript with a nicer syntax"; you need to approach it the same way you'd approach learning any new language.
The implied "only" in that statement seems preposterous to me. For one thing, it's both an over- and understatement of CoffeeScript's featureset, and for another, it's sort of like saying "The printing press just let people print books faster ∗yawn∗."
(Well, OK, so CoffeeScript isn't quite the leap that the printing press was, but you get the point.)
string interpolation
multi-line strings
comprehensions
guarded loops (hasOwn...)
easy constructor/prototype definition (class)
avoiding global leaks/shadowing
function binding
splats, defaults
safe usage of reserved names
existential operator
chained comparisons
READABLE regular expressions blah = "crayfish"
pat = ///#{blah}///
is equivalent to: pat = /crayfish/
This is in the docs, but if you blinked, you'd miss it.I've worked on enough projects that I've come to view syntax as a cost and not a feature. It can sometimes be worth paying that cost if it gets you lots of features - but all the features you list already exist in Python, and presumably can be translated just fine by PyJS. Why an entirely new language syntax instead of something that you're likely using on the server as well? Or is CoffeeScript basically Javascript with Ruby syntax?
The difference between source and compiled code is much much closer. CS has been designed as a pure abstraction of JS, whereas those other projects take full-fledged languages with all of their idoms and features and make JS out of it. I agree that new syntax is a cost, and in general that we should fear abstractions. PyJS seems like it's more of a web framework (as is GWT) rather than a pure language compiler.
Python is a beautiful language, but just code in JS if you're working on the front end and are wary of CS. I'm a capable JS developer and it took me almost no time to get started with CS. Sure, you hit snags here and there with oddball situations and sometimes you have to check the output, but the benefits definitely outweigh the learning costs.
Not all:
- string interpolation is way more flexible in CS
- splats are sort of partially covered by several python features, but not nearly so elegantly
- the existential operator is not found in Python at all; you have to test against None instead. Also, Python does not have anything like the "soak" feature that the existential operator does.
- No really, readable regex. Python still doesn't even have a literal notation for regexes, but CoffeeScript allows multiline literals with comments, ignored whitespace, and interpolation.
Also, I don't think that lowboy mentioned multiline lambdas, but yeah, multiline lambdas.
I once thought as you do that syntax is secondary and that what really matters is functionality. That was before I started using CoffeeScript. The thing is, syntax isn't important for the sake of making it a pleasure to write. It's important for making it a pleasure to read. And odd scoping scenarios notwithstanding, CoffeeScript is exceptional in that regard.
Here's an example. String interpolation in CoffeeScript is incredibly simple:
console.log "Hello #{name}, how are you doing?"
translates to console.log "Hello"+name+", how are you doing?"
That's all. Typing it out, CoffeeScript saves you one keystroke. But there is a subtle boon to readability in the interpolation syntax. The first line says "We're placing the result of an expression here". The second line says "We're ending the string, concatenating the result of an expression, and then we're going to concatenate another string to that." The canonization of the idiom into syntax lets your brain relax and stop trying to translate.I used to use Python when I needed a more powerful calculator, or when when I needed a script to automate something tedious. Now I go to CoffeeScript for both of those use cases.
It demonstrates that CoffeeScript's `do` feature provides a convenient way to create a new scope for particular variables (a thing only possible in JavaScript via an immediately-invoked-function, at least, until `let` arrives) ... which is true, but is also an orthogonal concern to whether or not CoffeeScript's normal mode of variable scoping is lexical.
In many of the examples in this post:
do (foo = 'outer') ->
do (foo = 'inner') ->
... all of the variables are local to their function, making it a moot question whether the language is dynamic or lexical. Just as in JavaScript: function() {
var foo = "outer";
function() {
var foo = "inner";
}
}
... it would make no difference if the language had lexical or dynamic scope, because there are no variables present here that would need to reach outside of their enclosing function in order to perform variable lookup.Lexical vs. dynamic scoping only comes into play when you refer to an external variable. Whether or not that variable is or can be shadowed is beside the point. Putting parameters aside ... In JavaScript, the variable's identity is the closest "var"-declaration of that variable. In CoffeeScript, the variable's identity is simply the name of that variable, wherever it might occur in the current lexical scope. Both ways are lexical, not dynamic.
// In dynamically scoped JS...
var f = function() { alert a; }
f(); // would throw an error as expected, but...
var g = function() {
var a = 5;
f(); // would now work because `a` now exists.
} // The scope of `a` ends here though, so...
f(); // would throw again, but if we give life to the dinamically scoped `a` again...
var a = 6;
f(); // now works! Printing 6 as "expected" (at least after swallowing dynamic scope). x = 'lexical'
acid = ->
alert "#{x}ly scoped"
test = (x) ->
acid()
test('dynamic') methods = ['remove', 'show', 'hide', 'stop']
for method in methods
do (method) -> # *see notes below
Frame::[method] = ->
element[method]() for element in @elements
*As @jashkenas points out, under the hood, `do` creates a new function (and thus a new scope). Invoking `do` with a parameter list (here "(method)") takes a variable from the outer scope and uses it for a parameter name within the newly created scope. So within that new function, references to `method` will not refer to the `method` variable of the loop body, but to the new function's `method` parameter. `do (method) ->` basically compiles to: `(function(method){ ... })(method)`.Of course you can't rely on people having JavaScript 1.7 in their browser. And even if their browser supports it, if the script tag is not declared correctly, YOUR JavaScript won't be understood to be JavaScript 1.7. So people avoid JavaScript 1.7 features.
I now return you to your regularly scheduled flame war.
I find that unconvincing -- I like one of Walter Bright's maxims: the simple path should be safe. Unless you're scrupulous about do(), if you do an assignment without the previous definition, there is no error. I'm happy that CS supports it in some capacity though!
Second, CS is supposed to be better that Javascript -- and I prefer many of its features. Apparently claiming that conflating variable definition and assignment is fine, because JS complicates scoping in other ways, is missing the boat. I think everyone agrees that JS's scoping is less than ideal, to put it politely.
Finally, I don't think we should be posting angry articles. The ad hominems and barbs in there aren't raising the level of discourse. :(
Also, users of cs shouldn' t have to think about how one language "transpile" to another. Otherwise what would be the point of using cs? If coffeescript designers have provided a clear documentation of their language behaviour, users should refer to it, and not make wild guesses of how it behaves, or how it translates to js.
That said, I'm not an expert of either language.
> CoffeeScript is a little language that compiles into JavaScript. Underneath all those awkward braces and semicolons, JavaScript has always had a gorgeous object model at its heart. CoffeeScript is an attempt to expose the good parts of JavaScript in a simple way.
> The golden rule of CoffeeScript is: "It's just JavaScript". The code compiles one-to-one into the equivalent JS, and there is no interpretation at runtime. You can use any existing JavaScript library seamlessly from CoffeeScript (and vice-versa). The compiled output is readable and pretty-printed, passes through JavaScript Lint without warnings, will work in every JavaScript runtime, and tends to run as fast or faster than the equivalent handwritten JavaScript.
Python used to be my favorite language, but I'm currently working on a project that is CoffeeScript on the client and Python on the server, so I'm getting plenty of time to compare them side by side. It would be difficult to overstate how much I prefer working with CoffeeScript.
The thing is, this scope situation, which is admittedly not as elegant as it could be, is really a rare situation to run into. If it happens to you often, it generally means that you need to give your more global variables less vague names.
Do you mean:
try:
foo = '42'
except Whatever:
raise
print "foo is %s" % foo
If so, I really like that feature. It's much clearer to me than what I'd write in another language: foo = None
try:
foo = 42
... def foo():
x = 5
def bar():
print x
def baz():
x = 6
print x
bar()
baz()
print x
Which prints 5, 6, 5. If baz tried to print x before assigning, it would be an UnboundLocalError.Edit: imagine what a failure case it would be for the web if Oracle owned the IP rights to a core web standard and actively asserted them. Yikes.
According to the article, CoffeeScript does not actually have lexical scope for non-parameter local variable assignment, which is what most people are talking about when they say "lexical scope". CoffeeScript has as much lexical scope as Emacs 23 did (after all, you could use lexical-let, right?). This article is a "holding it wrong" defense of CoffeeScript.
If the tone of this article is typical of the CoffeeScript community, I think I'll steer clear from the whole language and stick to JavaScript when I need web scripting.
var k = 1;
var foo = function(){
var k = 2;
var foo = function(){
if (Math.random() > .5){
var k = 3;
}
trace(k);
};
}
[...]
("transpiles" to) [...]
var k = 1;
var foo = function() {
var k1 = 2;
var foo1 = function() {
if(Math.random() > .5) {
var k2 = 3;
}
console.log(k1);
};
};http://news.ycombinator.com/item?id=2786720
> Rather than dumb luck, I think a more meaningful interpretation is that I was a piece of an evolving system, exploring one particular path in a damn hurry. That system contains people playing crucial parts. Academic, business, and personal philosophical and friendship agendas all transmitted an analogue of genes: ideas and concrete inventions from functional programming and Smalltalk-related languages.
let x = t1 in t2 <=> (λx.t2) t1
So, it behaves in a sane way (you have lexical scoping for local variables too). There is no "functional scope".http://coffeescript.org/#try:foo%20%3D%2042%0A(()%20-%3E%20f...
do (methods = ['remove', 'show', 'hide', 'stop']) ->
for method in methods do ->
Frame.prototype[method] = ->
for element in elements do ->
this[element][method]()
Error: In test.coffee, Parse error on line 5: Unexpected 'OUTDENT' do (methods = ['remove', 'show', 'hide', 'stop']) ->
for method in methods then do ->
Frame.prototype[method] = ->
for element in elements then do ->
this[element][method]()My intuition says it's reaching the end of the file before reaching the "end-of-block" unindent characters. I mean, the tokeniser should output enough outdents to get you back to indent-level zero when it hits EOF, but maybe it's not?
Humm...
CoffeeScript removes the latter, and the former is a lot easier to introduce once you have explicit variable declaration.
Seems kinda a circular argument to me.
http://www.reddit.com/r/programming/comments/zx137/coffeescr...
As is his original reply 2 years ago:
https://github.com/jashkenas/coffee-script/issues/712#issuec...
I think in the end because of the lack of block scope in js he ended up choosing the lesser of two evils really.
He does pass it off a little lightly though as if it's a feature. Jeremy's argument ultimately seems to boil down to 'it's easier for beginners', which is true and noble I think, but on the other hand it's a very easy way to introduce bugs.
It's easy to have a problem domain where you start slipping up, something like a doc with a list of related_docs that you enumerate through, very easy to call that inner variable doc again.
Personally one of the reasons I'm in the camp of favouring explicit variable declaration even if it is a bit of a pita when writing little scripts.
If cs ever becomes the next jQuery and everyone adopts it to make dealing with js that little less painful I could see demands for 'var' and 'use strict' being introduced. Though cs seems to be perpetually hovering on the cusp of mainstream acceptance. I think he sees less complaining about it as if you're at a level where you write enough code that if affects you, you'll be able to figure out and fix it yourself.
So am I, which is why the post explains exactly how to do it in CoffeeScript. I am glad he dropped "var," but I happen to disagree with his design choice for what to do with variables that are not explicitly declared.
But agree or disagree, it is not correct to say that there is no lexical scope in CoffeeScript when in reality people are debating how they want a leaky abstraction automatic variable hoisting feature to work.
https://github.com/jashkenas/coffee-script/issues/2552
Scheme interpreters have been optimizing away the cost for most cases since the 1970s.