Saturday 21 May 2016

Scope of Iteration Variable

The confusion with Closures inside loops and variables is well known, and I wrote about it years ago. When Microsoft decided to avoid this "problem" by making the scope of the iteration variable in foreach loops local to each iteration (but keeping the iteration variable in for loops global to the whole loop) I found it pretty confusing. In order to avoid a problem that only happened due to an incomplete understanding of closures, you change what I understood as the normal scope of a variable and make for and foreach loops behave differently. Let's see an example:

 var actions = new List<Action>();
  
  foreach (var i in Enumerable.Range(0,5))
  {
   actions.Add(() => Console.WriteLine(i));
  }
  
  foreach(var action in actions)
  {
   action();
  }
  
  Console.WriteLine("---------------------");
  actions = new List<Action>();
  
  for (var i=0; i<5; i++)
  {
   actions.Add(() => Console.WriteLine(i));
  }
  
  foreach(var action in actions)
  {
   action();
  }

The above code prints:

Reading about ES6, let and the scope of the iteration variable, I've seen that they have adopted the same approach that Microsoft took with the foreach loop. In ES6 loops (for, for-in and for-of) the scope of a let variable is the iteration itself, not the whole loop. It seems they have done this to avoid the "problem" with closures. It is explained here.
On each iteration, the loop creates a new variable and initializes it to the value of the variable with the same name from the previous iteration..
Let's see an example:

let items = [0, 1, 2, 3, 4];
let actions = [];
for (let i of items){
 actions.push(() => console.log(i));
}

for (let action of actions){
 action();
}

console.log("---------------------");
actions = [];

for (let i = 0; i<5; i++){
 actions.push(() => console.log(i));
}
for (let action of actions){
 action();
}

The above code prints:

Honestly, I find it counterintuitive that the scope behaves like that, but well, it's just a new rule that you have to learn. At least in ES6 the behaviour is the same for all loops, not like in C#, where for and foreach behave differently.

No comments:

Post a Comment