Skip to content

Loop variable referenced from inner function #1271

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

Closed
lowentropy opened this issue Apr 8, 2011 · 3 comments
Closed

Loop variable referenced from inner function #1271

lowentropy opened this issue Apr 8, 2011 · 3 comments
Labels

Comments

@lowentropy
Copy link

When I have a loop which produces inner functions referencing the iterator, those functions will refer not to their own copy of the iterator, but to an variable defined in the outer scope. The following test illustrates the problem:

test "iterator scope", ->

  actual = []
  expected = [1, 2, 3]
  funs = []

  for x in expected
    funs.push -> actual.push x

  funs[0]()
  funs[1]()
  funs[2]()

  eq actual, expected  

This produces the following javascript using coffee version 1.0.0:

(function() {
  test("iterator scope", function() {
    var actual, expected, funs, x, _i, _len;
    actual = [];
    expected = [1, 2, 3];
    funs = [];
    for (_i = 0, _len = expected.length; _i < _len; _i++) {
      x = expected[_i];
      funs.push(function() {
        return actual.push(x);
      });
    }
    funs[0]();
    funs[1]();
    funs[2]();
    return eq(actual, expected);
  });
}).call(this);

The loop variable is re-bound to each element of the array and is shared by all the functions produced. When they are called, they all use the last value assigned (in this case, the 'actual' array ends up being [3, 3, 3], instead of the expected [1, 2, 3]).

In previous versions of Coffeescript, the code would actually produce something like this:

(function() {
  test("iterator scope", function() {
    var actual, expected, funs, _i, _len, _fn;
    actual = [];
    expected = [1, 2, 3];
    funs = [];
    _fn = function(x) {
      funs.push(function() {
        return actual.push(x);
      });
    };
    for (_i = 0, _len = expected.length; _i < _len; _i++) {
      _fn(expected[_i]);
    }
    funs[0]();
    funs[1]();
    funs[2]();
    return eq(actual, expected);
  });
}).call(this);

Which works correctly.

@michaelficarra
Copy link
Collaborator

Yep, auto-closure-wrapping was removed just before 1.0 because it was broken. A proper fix is possible, but it's a very difficult patch. See #959 for the suggestions (and here's mine).

edit: Also, see the first sentence of the change log for 1.0.0.

@lowentropy
Copy link
Author

Thanks!

@michaelficarra
Copy link
Collaborator

Oh yeah, I never told you how to fix it. Here:

for x in expected
  do (x) ->
    funs.push -> actual.push x

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants