Take a look at this simple code:
eat = (x) -> console.log "nom", x
# dog only eats every second cat
feast = (cats) -> eat cat for cat in cats when _i % 2 == 0
feast ["tabby cat"
"siamese cat"
"norwegian forest cat"
"feral cat"
"american bobtail"
"manx"]
$ coffee a.coffee
nom tabby cat
nom norwegian forest cat
nom american bobtail
It seems the _i
variable is the current index. Is this a feature, bug, or NaN? I haven't heard anyone else talking about this, so I wonder if there's some reason I shouldn't use it in my code?
tldr-again; The author of CoffeeScript just told me I'm right: Don't use _i
.
14:29 <jashkenas> You shouldn't use internal variables. ... 14:42 <meagar> I was hoping something more deeply involved in the language would be able to put some authority behind that opinion 14:43 <meagar> ... I was basically hoping for an authoritative "don't do that" 14:44 <jashkenas> you just got it ;) 14:44 <jashkenas> for item, index in list -- there's your reference to the index.
tldr; This is at best an undocumented feature for which a functionally equivalent documented feature exists. As such, it should not be used.
Your argument of "less typing" is highly dubious; compare:
for x in [1, 2, 3] when _i % 2 == 0
console.log "#{_i} -> #{x}"
for x,i in [1, 2, 3] when i % 2 == 0
console.log "#{i} -> #{x}"
feature, bug, or NaN?
None of these things; it's undefined behaviour. You are assuming that _i
will be the variable used for iteration in the compiled JavaScript.
You definitely shouldn't use _i
, or assume _i
will be defined. That's an implementation detail, and they're free to change it at any time. It's also won't be _i
if your loop is nested in another loop; it will be _j
or _k
etc.
Most importantly, you can do this exact thing without relying on the underlying implementation's JavaSript variables. If you want to loop with an index, just use for value,key in array
:
array = ['a', 'b', 'c']
console.log(index) for item, index in array # 0, 1, 2
Specifically, in your example:
feast = (cats) -> eat cat for cat, index in cats when index % 2 == 0
No need to guess, or make assumptions, about what Coffeescript
does. Just look at the compiled Javascript
. From the 'Try Coffeescript' tab:
feast = (cats) -> eat cat for cat in cats when _i % 2 == 0
produces
feast = function(cats) {
var cat, _i, _len, _results;
_results = [];
for (_i = 0, _len = cats.length; _i < _len; _i++) {
cat = cats[_i];
if (_i % 2 === 0) {
_results.push(eat(cat));
}
}
return _results;
};
...
feast = (cats) -> eat cat for cat, index in cats when index % 2 == 0
produces nearly the identical JS, differing only in that index
is used along with or in place of _i
.
feast = function(cats) {
var cat, index, _i, _len, _results;
_results = [];
for (index = _i = 0, _len = cats.length; _i < _len; index = ++_i) {
cat = cats[index];
if (index % 2 === 0) {
_results.push(eat(cat));
}
}
return _results;
};
Both work, but index
makes your intentions clearer to humans (including your future self). And as others have argued, it is good programming practice to avoid use of undocumented implementation features - unless you really need them. And if you are doing something funny, document it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With