
The worst thing about CoffeeScript - luu
http://walpurgisriot.github.io/blog/2013/12/16/the-worst-thing-about-coffeescript
======
tghw
The worst thing about CoffeeScript is that you are used to having scoping work
a different way?

Let's extend your example:

    
    
        >>> a = 1
        >>> def foo():
              print a
        >>> foo()
        1
    

So Python does take from the outer scope?

    
    
        >>> a = 1
        >>> def foo():
              a = 2
              print a
        >>> foo()
        2
    

Or not?

    
    
        >>> a = 1
        >>> def foo():
              print a
              a = 2
        >>> foo()
        UnboundLocalError: local variable 'a' referenced before assignment
    

Or it just doesn't work at all?

Next time, pick a better example. The answer to "What does Python do with a
variable from outside a function's scope?" is "It depends."

At least CoffeeScript is consistent.

~~~
aaron-lebo
It is a bigger issue than lack of familiarity.

It can lead to some really unexpected bugs. There is a reason most languages
don't do things this way.

[http://lucumr.pocoo.org/2011/12/22/implicit-scoping-in-
coffe...](http://lucumr.pocoo.org/2011/12/22/implicit-scoping-in-
coffeescript/)

~~~
wildgift
This is also a problem with javascript. Javascript should do automatic
variable shadowing but also throw a warning.

------
jashkenas
Certainly, by far the "most controversial thing about CoffeeScript".

Fortunately, CoffeeScript is open-source, and can be both easily changed, and
easily forked to suit your fancy. There are very nice versions (LiveScript and
Coco, for example), that change this behavior by introducing two different
types of assignment — if that sort of thing is your cup of tea.

... and if anyone has a specific proposal about how they'd like to see this
feature changed in order to improve it, feel free to open a ticket for
discussion — _it could very well happen_. For example, making this:

    
    
        topLevelFunction = ->
          path = "a/b/c"
     
        ... many lines of code go here ...
    
        path = require('path')
    

... into a compile-time error, with some sort of "Subsequent variable shadows
`path`" warning, that would be something worth talking about.

~~~
emn13
The fact that it's open source doesn't help: using and maintaining a similar
but subtly different version of coffeescript is worse than living with this
bug since it means your code is now incompatible with the rest of the world's.
Not to mention you're likely to train yourself to misread "real" coffeescript.

As to the ticket; A quick search would have revealed there are many tickets on
the topic of shadowing: [https://github.com/jashkenas/coffee-
script/search?q=shadowin...](https://github.com/jashkenas/coffee-
script/search?q=shadowing&ref=cmdform&type=Issues)

And this ticket covers this specifically [https://github.com/jashkenas/coffee-
script/issues/2697](https://github.com/jashkenas/coffee-script/issues/2697)
and was closed.

~~~
moomin
I think Jeremy is being a bit disingenuous here. There are many good reasons
for not wanting to fork a language, and getting a change made to CoffeeScript
is frustrating and glacial. To be clear, I understand why this is: bad
decisions are hard to reverse. Unfortunately, the specific issue raised at the
top is a poster-child for bad decisions that are hard to reverse.

------
spellboots
I use both javascript and coffeescript extensively and regularly, and I just
don't understand how this ever causes real problems to anyone - unless you're
writing 5000 line long spaghetti code coffeescript files with ambiguous
variable names, in which case lexical scoping is the least of your worries.

~~~
zackbloom
I have to agree, I will probably write 10k lines of coffeescript in the next
year, much of it in reusable and open source libraries, and OPs problem does
not seem to cause issues all the much in practice.

People have to distinguish between the simple issues that trip up beginners,
and the fundamental issues that plague you for life.

~~~
coldtea
> _I have to agree, I will probably write 10k lines of coffeescript in the
> next year, much of it in reusable and open source libraries, and OPs problem
> does not seem to cause issues all the much in practice._

Whether it causes any issues in practice for some particular programmer or not
(and 10K lines is not much even for ONE modern webapp, and you write them
broken down in multiple libraries), the thing is the behavior is logically
flawed in itself.

Like Javascript needing "var" to NOT make a global variable (instead of
defaulting on local vars) is.

Coffescript's behavior on this scoping issue is in the list of bad items that
includes goto, global variables and nullity.

------
agumonkey
Raganwald shows an interesting point of view on Coffeescript scoping in
relation to javascript
[https://github.com/raganwald/homoiconic/blob/master/2012/09/...](https://github.com/raganwald/homoiconic/blob/master/2012/09/actually-
YOU-dont-understand-lexical-scope.md)

------
standup75
Never had this problem with coffeescript until... today. Some hours after I
read this post. Thanks

angular.module("blabla", []).directive "bla", ->

template: """ <div>Whatever<div ng-transclude></div></div></div> """ scope:
true transclude: true restrict: "E" compile: (element, attributes) -> $input =
element.find "input" for attr, value of attributes.$attr $input.attr value,
attributes[attr] element[0].removeAttribute value

    
    
    		(scope, element, attributes) ->
    			$input = element.find "input"
    			# do something with $input
    			# now $input is global to all directive and that sucks

~~~
saraid216
Drop a "space space" in front of every line in your code and it'll format
monospace.

------
badman_ting
This is why I don't use it. I know JS well, I can think in JS. Changing the
syntax is fine but altering semantics in subtle ways would probably drive me
crazy.

~~~
tomphoolery
It doesn't really alter the semantics, if you recognize that CoffeeScript
_always_ adds `var` before variable assignments, you can just get around that
by attaching a variable you want to share to a higher scope by using
`this.whatever = 'foo'`. It's really not as hard as people make it out to be,
but there are still plenty of killer reasons not to use CS (for example, if
you don't feel like learning it...I mean JS is just as good, it does all the
same stuff).

~~~
DougBTX
CoffeeScript doesn't always add var before assignments though, sometimes it
does, sometimes it doesn't, depending on the variables in outer scopes.

This has two assignments, but only one var in the compiled JS:

    
    
        a = 1
        -> a = 2
    

This has two vars:

    
    
        -> a = 1
        -> a = 2

~~~
emn13
Much worse is this gotcha:

One variable:

    
    
        a = 1
        -> a = 2
    

Two variables:

    
    
        -> a = 2
        a = 1

------
james2vegas
It is funny to see Python in an example of 'good' variable scoping. How
exactly would you refer to the outer 'a' in that python example?

~~~
picomancer
The "global" keyword in Python binds a name to the scope of the top level of
the enclosing module. The "nonlocal" keyword is a Python 3.x feature which
binds a name to the next enclosing scope level.

So you would do:

    
    
        a = 1
    
        def contrived():
            global a    # I added this
            a = 2
            return a
    
        contrived() # 2
        a           # 2
    

The "nonlocal" keyword would give the same result if the outer scope is the
top level of the module. If the entire above code is itself enclosed in a
function like so:

    
    
        def enclose():
            a = 1
    
            def contrived():
                nonlocal a
                a = 2
                return a
    
            contrived() # 2
            a           # 2
    

You need "nonlocal" to refer to the "a" that's a local variable in enclose().
I.e. "global" gets you the top level, "nonlocal" gets you the next enclosing
level.

The keyword is only necessary when you're _assigning to_ an out-of-scope
variable. If you remove the assignment statement "a = 2" from the function,
the program will correctly read and return the value of a from the enclosing
scope.

In other words, by default the set of names considered to be local variables
in a function is the set of names that are the target of an assignment
statement. Usually the default behavior is what you want, but if you wish to
override it, use the "global" or "nonlocal" keyword.

From a design standpoint, I've learned over the years that global / nonlocal
keywords are a "smell" that frequently indicates poor architecture, usually of
the sort that the oft-cited "global variables are evil" doctrine is intended
to protect against. Code that extensively uses these keywords should usually
be refactored into a class, with the formerly global names instead being
attributes (member variables).

~~~
cursork
Not completely seriously.. This is why some of us prefer Perl ;)

    
    
        my $a = 1;
        
        my $contrived = sub {
            my $a = 2; # new lexical variable $a
            $a = 3; # if the my above wasn't there, outer $a would be 3
            ...
        }
    
        say $a; # 1
    

Explicit declaration of the scope of a variable at initialisation is awesome.
'global' and 'nonlocal' feel weird/less consistent/like a workaround. People
are used to creating variables 'on the fly' in Python / CoffeeScript - both
break (for a certain value of 'break') certain assumptions in certain
circumstances.

I've never heard anyone ever complain about perl's lexical 'my'.. It even
catches - with a warning - multiple my declarations that will clobber each
other. You don't see that in other languages that often.

~~~
picomancer
If you've ever programmed in JavaScript -- and most people have, it's the
language of the Web -- you will know that it is _very easy_ to accidentally
omit the "var" keyword and unintentionally pollute the global namespace. Such
a bug likely will not manifest until someone else writes completely unrelated
code that uses the same variable name by coincidence.

Explicit declaration of the scope of a variable at initialization leads to
unintended global namespace pollution when omitting the declaration is legal
and causes the variable to be placed in the global scope, which can be
difficult to diagnose because it is usually completely silent until unrelated
code happens to interact by using the same name.

From a language design standpoint, this means that you should either require a
declaration which determines scope (C or Java), or have local be the default
for variables whose scope is undeclared (Python).

> I've never heard anyone ever complain about perl's lexical 'my'

That's because programmers who care about good syntax in their languages don't
use Perl.

~~~
cursork
> Explicit declaration of the scope of a variable at initialization leads to
> unintended global namespace pollution when omitting the declaration is legal
> and causes the variable to be placed in the global scope

Ah, but it's _not_ legal with 'use strict;' (which _everyone_ uses)

> That's because programmers who care about good syntax in their languages
> don't use Perl.

And Lisp has too many parentheses. I clearly just like my code to be
illegible.

~~~
emn13
Yeah - back when coffeescript was new this was an argument, but with "use
strict", the likelihood of accidental mis-assignments is actually considerably
slower in javascript than in coffeescript. In javascript you'll get an error
assigning to a non-existant variable you meant to be global; in coffeescript
you'll get a local varaible and that means subtly buggy code.

~~~
theOnliest
I think cursork was talking about 'use strict' in Perl; he said that everyone
uses it. 'use strict' in JavaScript is much rarer than it is in Perl, at least
in my experience.

~~~
emn13
Well, I _always_ use it when I have the chance: it's easy to turn on, and it
catches nasty bugs. Why wouldn't you turn it on? Even if you have a large
legacy code base, it's still a a win, since you can turn it on function-by-
function.

------
bestest
you're doing something wrong if this is an issue for you.

I personally would like to apologize to everyone for dissing CoffeeScript in
the past, here (thus my low karma) and elsewhere.

I'm working with a new customer now, three weeks already. and they're using
CoffeeScript. and I had to get over myself and start using it. and it's
amazing. I've been so stubbornly blind to be against it without actually
trying it out yet cursing and flaming against it. it's so much faster to work
with it, it reduces the boilerplate code, it simplifies everything.

and, yes, I still think you need to be able to know and use JavaScript very
good before taking on CoffeeScript -- otherwise topics like the one we're
discussing here arise.

~~~
coldtea
> _you 're doing something wrong if this is an issue for you._

Of course he's doing something wrong. That's the whole point of TFA. That the
language, due to a bad design decision, permits this case of doing something
wrong, when it shouldn't.

Like Javascript making variables global by default (unless you use var). If
this is an issue to you, you're doing something wrong (namely, you're
forgeting to add "var"). But the language does an even bigger mistake by
permitting this to happen.

~~~
bestest
all I can say is "use strict";

~~~
coldtea
I do! But that's not available in CS, right?

------
drinchev
For me there is no worst thing in CS. The thing that bothers me now is that
there is no practical standard on documenting the code similar to JsDoc. Yes
there are a couple of ways to do that, but in fact meta-programming helps not
only for cleaner code, but also Closure Compiler, any IDEs ( WebStorm
autocomplete ) and also it helps make the code more readable. Anyway in every
project that I work I usually try to convince my team to switch to
CoffeeScript.

Big thanks to jashkenas, because ... CS is awesome!

------
nicholaides
First of all, that's hardly a bad thing about CoffeeScript. It's very
consistent and is a tradeoff of not having to use var everywhere to define
your variables.

If you want to complain about CoffeeScript, there are some much more
fundamental issues. For example:

\- I can write `foo.bar baz, bat` but if I call it with no arguments I have to
use parens. `for.bar` will just return the function. I understand why it's the
case, but it sucks.

\- Syntax is so flexible that sometimes it still compiles when you make a
mistake.

~~~
bchar
"\- I can write `foo.bar baz, bat` but if I call it with no arguments I have
to use parens. `for.bar` will just return the function. I understand why it's
the case, but it sucks."

Interestingly, if you write "do foo.bar" you don't need to use parens and it
will work as expected. I think this is a bit more idiomatic.

------
jbuzbee
Worst thing? I was expecting to see discussion on the use of white-space
instead of the obviously superior curly braces! :-)

------
thom_nic
I believe there is a coffeelint plugin that will warn you if you reassign a
variable from an outer scope

------
leafo
MoonScript introduces a special construct for handling this exact situation:
[http://moonscript.org/reference/#the_using_clause_controllin...](http://moonscript.org/reference/#the_using_clause_controlling_destructive_assignment)

------
pera
iirc LiveScript don't have this issue with scoping

~~~
elclanrs
In LiveScript there's a := operator. From the docs -- "However, unlike
CoffeeScript, you must use := to modify variables in upper scopes" \-- I find
LiveScript overall a better alternative to CoffeeScript. If you're into CS for
the sugar, then you'll love LS.

~~~
naturalethic
Much love to LS! Been using it almost exclusively for over a year.

------
wprl
Is this really news?

------
elwell
Don't use semicolons in CoffeeScript!

