Monday, February 27, 2012

The scope of for loop variables

What is the output of the following code?
procs = []
for lang in ["Ruby", "Scala", "Haskell"]
  procs << lambda { p lang }
end
procs.each(&:call)
Do you expect the following output?
"Ruby"
"Scala"
"Haskell"
In Ruby 1.9.3, the actual output is as follows:
"Haskell"
"Haskell"
"Haskell"
This is because a for expression doesn't introduce a new variable scope. So all Proc objects closes the same variable lang, whose value is "Haskell" after the evaluation of the for expression. If you use a block instead of the for expression, you can see the expected output, because the block introduces a new variable scope, and each Proc object closes a distinct variable.
procs = []
["Ruby", "Scala", "Haskell"].each do |lang|
  procs << lambda { p lang }
end
procs.each(&:call)
This behavior of for expressions is sometimes harmful, so I have filed a ticket to fix it.
Ruby was originally designed as an imperative language (of course, it is an imperative language even now), so it was reasonable to modify for loop variables by side effects.  However, people prefer functional styles now, so it's time to reconsider the behavior of for expressions.

No comments: