Phorce
Phorce

Reputation: 4642

Why does CoffeeScript compile to unidiomatic JavaScript?

I'm playing around with Coffee script (I'm new to this "language") and tried just a very basic example:

x = [1, 2, 3] 
for element in x 
  console.log(element)

This does what it says, for each of the elements it outputs it to the console. It's only when I took a lot at the Javascript it compiled to that I can't understand why they do this:

(function() {
  var element, i, len, x;

  x = [1, 2, 3];

  for (i = 0, len = x.length; i < len; i++) {
    element = x[i];
    console.log(x[i]);
  }

}).call(this);

Would it not just be better and more natural to do:

for(i = 0; i < x.length; i++)
{
   console.log(x[i]);
}

Why does coffee script compile to such a way that does not seem natural? If I was writing the same exact code in Javascript, I would follow the method that I did, but I might be wrong in that the way I did it (Javascript) is not the most efficient or "natural" and the coffee script method is a better solutions.

Upvotes: 0

Views: 109

Answers (2)

Marc Greenstock
Marc Greenstock

Reputation: 11668

Firstly, CoffeeScript builds everything in a closure by default, that would be these lines:

  (function() {
  }).call(this);

Closures prevent variables leaking out of the script. If you want to export your script as a global, then you need to explicitly attach it go a global, like window.

Next is the line:

var element, i, len, x;

It is considered good practice to declare your var's before using them (at least according to jslint).

Your definition of x of course:

x = [1, 2, 3];

Now the loop:

for (i = 0, len = x.length; i < len; i++) {
  element = x[i];
  console.log(x[i]);
}

Defining len first prevents the loop constantly looking up what x.length is. Not a huge speed difference with only three items, but an array with 10k, well...

The console.log is obvious, but the element variable is defined because that is the name of the variable you specified when you wrote your loop. And it uses i instead of element to iterate mostly because it's standard practice to use i for iteration.

So you see everything has a legitimate reason, albeit a little verbose for some tastes.

Upvotes: 1

JLRishe
JLRishe

Reputation: 101662

Basically you are expecting the CoffeeScript compiler to do acrobatics that would be really complicated.

Consider this CoffeeScript:

for a in x
  console.log(a)
  for b in a
    console.log(b)
    callFunc(b)

This transpiles to:

for (i = 0, len = x.length; i < len; i++) {
  a = x[i];
  console.log(a);
  for (j = 0, len1 = a.length; j < len1; j++) {
    b = a[j];
    console.log(b);
    callFunc(b);
  }
}

It's very easy to see how the original CoffeeScript corresponds to the resulting JavaScript:

for [variable] in [array]
    [body]

becomes

for (var [letter] = 0, len = [array].length, [letter] < len; [letter]++) {
    [variable] = [array][[letter]]
    [body converted to JavaScript]
}

So when translating a for comprehension into JavaScript, (almost) all the compiler needs to worry about is this simple correspondence, and then it can work inward and process the body as a separate and independent operation.

Also, the len = [array].length caching is done for performance. This isn't entirely pleasant to look at, and most people wouldn't code this way, but producing beautiful JavaScript is not the primary goal of CoffeeScript. It produces efficient code with the assumption that people won't generally be reading the generated JavaScript.

What you are suggesting is that it take that original code and turn it into:

for (i = 0; i < x.length; i++) {
  console.log(x[i]);
  for (j = 0; j < x[i].length; j++) {
    console.log(x[i][j]);
    callFunc(x[i][j]);
  }
}

In order to convert console.log(b) into console.log(b);, the compiler doesn't need to know anything about the code surrounding that statement. In order to convert console.log(b) into console.log(x[i][j]);, the compiler would have to take everything around that statement into account. It would give the compiler an extremely complicated job to perform and wouldn't really provide any benefit.

Upvotes: 4

Related Questions