

Actually, you don't understand lexical scope - fogus
https://github.com/raganwald/homoiconic/blob/master/2012/09/actually-YOU-dont-understand-lexical-scope.md#actually-you-dont-understand-lexical-scope

======
ynniv
The goal of this diatribe appears to be to justify CoffeeScript's design, but
to me it counts against it. To be fair, I have never thought CoffeeScript was
worth using, but here's the crux:

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.

~~~
raganwald
_CoffeeScript has different semantics from JavaScript, and the obvious
expansion of CoffeeScript back into JavaScript is a wrong expansion_

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.

~~~
ynniv
_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._

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.

~~~
raganwald
As every debate ought to say, we're each entitled to our own opinions, but not
our own facts. My opinion--which I believe I have demonstrated to be a fact--
is that it is possible to write sane CoffeeScript that is lexically scoped and
not subject to misinterpretation.

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.

~~~
ynniv
My assertion is that CoffeeScript is not an alternative syntax for JavaScript
because of differences in scoping. My opinion is that this is not a good
design choice because it increases the separation from the original language,
requiring that someone who already knows JavaScript additionally learn
CoffeeScript.

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.

~~~
lowboy
In practice, the scoping difference that you run into 99% of the time is that
implicit _var_ keywords are in front of variables that haven't been seen in
the current scope, and you have to assign globals to your top level scope (
_window_ in the browser). How long did it take me to bridge that separation
from the original language? Oh, about a minute.

~~~
lttlrck
you shouldn't have to have a minute or even second considering the
idiosyncracies of the underlying language...

~~~
lowboy
I've made a one-time investment of a minuscule amount of time and have been
raking in the saved time and effort from not having to type var statements or
track down lack-of-var bugs.

Pure profit for me, but as always YMMV.

------
jashkenas
I'm afraid that this post, while well intentioned, is actually clouding the
issue it's attempting to clarify.

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.

~~~
epidemian
Agreed. Maybe an example of how JavaScript/CoffeeScript would behave if it had
dynamic scope semantics could help:

    
    
      // 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).

~~~
raganwald
From the vicious flame-fest that precipitated this post:

    
    
        x = 'lexical'
    
        acid = ->
          alert "#{x}ly scoped"
    
        test = (x) ->
          acid()
      
        test('dynamic')

------
btilly
One addendum. As of JavaScript 1.7 there is yet another way of declaring a
variable. It is called let, and makes var work the way it should have worked
all along.

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.

~~~
raganwald
+1. I think that I edited out a sentence where Nickieben used let as an
example of JavaScript people agreeing that var was feciform.

~~~
andrewcooke
ok, this is the second hit on google for "feciform" (third is "vomiting
feciform matter" while first is "FECI form"). is this really a word? if not,
what on earth did you mean?

~~~
raganwald
Something taking on the form of feces ;-)

------
dmpk2k
_But that is unnecessary in CoffeeScript, because if you want lexical scope,
you already have lexical scope:_

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. :(

------
prunebeads
All this comes from the fact that people think for some reason that
coffeescript is a javascript variant. Obviously, it has been demonstrated that
it's not: syntax is different, semantics are different.

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.

~~~
adgar
You might be wondering why people think that. Here's why: it's the _entire
point of CoffeeScript_. According to the first two intro grafs at
coffeescript.org:

> 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.

~~~
prunebeads
That's a misunderstanding it seems. The real meaning is that "under the hood,
it's javascript", and not "it's like javascript". People read "It's just
Javascript", and jump to the second meaning, without thinking of the first,
but the rest of the text is pretty explicit.

------
run4yourlives
Can we just implement Python (or anything else, really, hell I'll even take VB
at this point) in the browser already and get rid of this monstrosity of a
language? Please?

~~~
mistercow
Really? Python's handling of reaching into the outer scope of a function is
just as confusing (although I hear it is fixed in 3000?).

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.

~~~
jrockway
_Really? Python's handling of reaching into the outer scope of a function is
just as confusing (although I hear it is fixed in 3000?)_

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
       ...

~~~
ostso
Perhaps mistercow means things like

    
    
      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.

------
quotemstr
I found the tone of this piece incredibly off-putting. The author substitutes
smugness for reason. (Also, his frequent grammatical errors and "swinging
dick" comments do not exactly connote a sense of maturity and experience.)

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.

~~~
raganwald
This post is not typical of the CoffeeScript community, and the flame-fest
that provoked it is not typical of the JavaScript community. Amusing outliers
at best.

------
jdonaldson
I really appreciate how Haxe (which targets js) handles this situation. If you
want to write js, and stick with block level scoping, it's a great option.

    
    
            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);                                           
                    };                                                                 
            };

------
agumonkey
I just had an quepiphany. Is it the lambda/scheme culture of defining scope
via lambda and application that made javascript having 'only' functional scope
?

~~~
k3n
In a nutshell, you could probably reduce it to that.

<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.

~~~
agumonkey
Thanks, I forgot this thread.

------
howeyc
tl;dr - CoffeeScript devs know that if you want lexical scoping you use do.

[http://coffeescript.org/#try:foo%20%3D%2042%0A(()%20-%3E%20f...](http://coffeescript.org/#try:foo%20%3D%2042%0A\(\(\)%20-%3E%20foo%20%3D%2043\)\(\)%0Aalert%20\(foo\)%0A%0Abar%20%3D%2042%0Ado%20\(bar%20%3D%2043\)%20-%3E%0A%20%20bar%20%3D%2044%0Aalert%20\(bar\))

------
fogus
This may be the first time that I've learn any useful information (do) during
one of these CoffeeScript scope battles.

------
josteink
The tone of that rant tells me all I need to know about the Coffeescript
community and developers. And yes, that means I'll stay clear of it.

------
comex
Actually, I don't understand CoffeeScript. The example from the post doesn't
even compile, using CoffeeScript 1.3.3:

    
    
        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'

~~~
grncdr
I noticed that while reading it as well, as it's one of the (few) things that
bugs me when writing coffeescript. It should have a 'then' between the
conditions of each 'for' loop and 'do', because that is the only way to
include the loop body (the 'do' statement) on the same line as the loop
conditions.

    
    
       do (methods = ['remove', 'show', 'hide', 'stop']) ->
          for method in methods then do ->
            Frame.prototype[method] = ->
              for element in elements then do ->
                this[element][method]()

------
kzahel
I personally can't stand the style of javascript where each for loop causes a
new function scope. It makes debugging extremely annoying. The use of _.each,
forEach constructs with function parameters makes debugging all that more
difficult. I happily manually type all my for(;;) loops in javascript because
when I hit my assertions, I have no problem determining exactly what was going
on.

~~~
raganwald
CoffeeScript has loops and comprehensions for your function-free programming
needs. The issue here is what to do when you want to shadow a variable. I am
open to correction, however I think in both JavaScript and CoffeeScript you
need a function invocation to create a new lexical scope.

------
jhrobert
OK, so coffeescript is really slow at implementing "local variables".

Humm...

~~~
raganwald
<https://github.com/jashkenas/coffee-script/issues/2552>

------
ajanuary
The argument for not introducing a 'var' keyword equivalent seems to be
"Javascript's implementation sucks", completely glossing over the fact that
really it's the lack of block scoping and the presence of undefined that leads
to variable 'hoisting' and related issues.

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.

------
mattmanser
Jeremy's reply and the counter reply on the original proggit thread are both
very interesting, as is the whole thread if you haven't read it, though the
tone is abrasive at times:

[http://www.reddit.com/r/programming/comments/zx137/coffeescr...](http://www.reddit.com/r/programming/comments/zx137/coffeescript_devs_dont_understand_lexical_scope/)

As is his original reply 2 years ago:

[https://github.com/jashkenas/coffee-
script/issues/712#issuec...](https://github.com/jashkenas/coffee-
script/issues/712#issuecomment-430673)

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.

~~~
raganwald
_I'm in the camp of favouring explicit variable declaration_

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.

~~~
benth
So there is lexical scope, but it requires "do" which means an extra function,
something that wasn't required in JS. You don't get lexical scope "for free"
in CoffeeScript, there's overhead, unlike in JS. Is that right?

~~~
raganwald
Yes, and that is a concern I share:

<https://github.com/jashkenas/coffee-script/issues/2552>

Scheme interpreters have been optimizing away the cost for most cases since
the 1970s.

------
nicolasmiller
Well, actually...

------
mortdeus
Dart!

