0

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.

6
  • 1
    len = x.length means they are caching the value of x.length to len, rather then calculate the length of x on each run through the loop. it may not seem the "natural" way, but its the optimal way, which is want you want from a compiler/preprocessor. they are creating a clsure/sef to avoid globals. (once you have more code in the example it makes more sense) Commented Feb 26, 2015 at 12:51
  • Check this tool: js2.coffee it is very usefull with comparing JS to CoffeeScript. Commented Feb 26, 2015 at 12:53
  • 1
    @atmd - hit the nail on the head - the keyword here is optimal. The transpiled code is meant to be optimal and not necessarily the "natural" way a developer might write the code. Commented Feb 26, 2015 at 12:53
  • 4
    "natural" doesn't always coincide with "efficient", unfortunately. But here, "efficiency" might not be the first concern, but rather correctness. The code is wrapped inside an anonymous function because in JavaScript, only functions have scope, not blocks. Commented Feb 26, 2015 at 12:54
  • 1
    I really don't see why the question is down-voted. It's an honest question, with a simple answer, from someone who is new to the language, stated their assumptions, then mentioned they might be wrong. To me, a downvote smacks of a knee-jerk reaction to "coffeescript haters". Commented Feb 26, 2015 at 17:10

2 Answers 2

4

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.

Sign up to request clarification or add additional context in comments.

2 Comments

+1. Coffeescript is a simple-minded transpiler, not an optimizing compiler with heavy static analysis and "acrobatics".
1

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.

1 Comment

"but an array with 10k, well..." – there it could actually be worse to do that manual "optimization"…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.