-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
inner scopes don't shadow variables; large potential for bugs #238
Comments
+1 for local. |
Thanks for the excellent ticket -- hopefully we'll get some discussion. The current behavior of CoffeeScript is by design, it's working the same way as it does in Ruby (and it seems, according to your explanation, in Python as well) -- with a slight JavaScript twist in that declaration always happens before evaluation. Programmers in those languages get by just fine without It's certainly possible to accidentally blow away an outer variable within an inner scope, but (in Ruby and Python), this turns out not to be a very common problem. If you don't nest functions too deeply, don't introduce a lot of globals, and give things proper names, it never arises. In JavaScript, having So, I'd like to make the argument that having named variables observe consistent lexical scope -- you can be sure that you're dealing with the same variable in inner functions, and not something unrelated, is a more pleasant experience. Allowing |
no and no. ruby has:
Ruby advantage is that you don't need that many local variables; due to class and instance variables. python is even different:
Which is weird, but then python doesn't have lexical scoping at all. So don't look at python.
I disagree. Doing it once is quite natural, fresh variable? use (As appossed to the coffee-script flowchart: fresh variable? ensure your name is unique in all enclosing scopes.) Your final argument is a matter of taste. But inside inner scopes, the more-local variables are, the more relevant to the code they are. I would argue it is easier to keep track of the more-local variables, as apposed to all the more-global variables. And any meaningful global variables you would give meaning full names. E.g.
So how do I know not to use 'old' as tmp variable name? However; it looks much better (much more declarative) if you don't have to write |
Python also has the |
grayrest -- To create globals in CoffeeScript on purpose, simply attach your variable to the global object ( As for Ruby's variable options -- Yes, you need to ensure that your name is unique to the enclosing scope, but you should be doing that in any case. Scopes should be short and sweet, and not nested deeply. CoffeeScript is making it easier to write well structured code (by omitting This is really the same issue as the nested for loop. In JavaScript:
|
The way Coffee scopes variables now is working out just great. I have not had any issues with it so far. Being able to access outer variables from inner scopes is useful at times:
In this case, if Coffee defines a local variable to
|
Its not global variables that are the problem; its the local variable that is accidentally used in an outer scope too, creating havoc. As said, the way it currently works is a choice, and it is not even a bad choice per-se. It would not be my choice, but that is besides the point. But it is a choice that you would want to explicitly document as a potential pitfall.
Probably; unfortunately any trivial example will resort to bad-style/good-style debate. Which is not the debate at all. Quite sure there can be code that must be considered very good style, but still runs into this accidental variable sharing problem.
Also for completeness; indeed that would create a true global. But for static scopes, any variable defined in a more outer scope is 'global' (also called free). What we used to call global variables come from languages that cannot do closures (like c or java). But talking about those does not really contribute to the issue at hand. |
Great. We're agreed then. I'll update the documentation section that talks about Assignment & Scope to mention this potential pitfall this evening. Thanks for bringing it up. |
Nothing would change in how your example works.
That is the problem, what if you think you are using a different variable, but you accidentally did not and change state somewhere where you didn't intend to do so? If you want a new variable; you can mark that using (And if coffee-script would add this, any coffee-script-lint would immediately warn on any variable introduction that skips the |
Cool :) I think coffee-script is an awesome project, very pragmatic syntax choices. Great inspiration. Keep up the good work! |
Docs added, closing the ticket -- and it's good to have this on record, so it can be referred to in the future. |
FWIW, JavaScript 1.7 (Mozilla extensions) introduced |
There is a slight conflict when there is no explicit variable introduction and variables are mutable (in any language). You might accidently re-use a more outer variable in a inner function, while you wanted to shadow it instead.
trivial example:
This example is trivial; but in the real world this happens easily with only local meaningful variables like an
n
, orprev
or what have you ...The trouble is:
foo: "hello"
stands for assign "hello" to foo variable and introduce foo as a variable (in the current scope) if it doesn't exist already.But there are two distict uses: (1) you need a new (local) variable; (2) you want to assign a new value to a (more) global variable. Trouble is with (1): you need to keep in your head a list of all more global variables.
Using
var x: "value"
prevents this. And you could make thevar
optional or use something likelocal
to make this explicit. Or you can usex: "value"
as introduction and assignment, and usex = "newvalue"
as variable change (but not introduction). Or just document the behavior and its shortcomings clearly and learn to live with them. (Or you can do what python does, if the first reference is a read, it is a more global variable, otherwise it is a local variable; but please don't.)I'm willing to bet this will cause someone to waste a day of debugging, which turns out to be a unexpected sharing of a more outer variable.
The text was updated successfully, but these errors were encountered: