Evils of the For Loop in Ruby
blog.grayproductions.net
blog.grayproductions.net
It's nice that James managed to dig up some technical reasons that extend beyond the idiomatic argument, as it may help push along those who aren't moved merely by the "It's not the Ruby way" argument.
Either way, there is at least one case where the custom block iterator doesn't cover the usage of "for" - where you need the variables outside the scope of the loop.
Needless to say, I don't.
For one, there is a large difference between the spaghetti code that can result from liberal and injudicious goto usage vs. whatever you might encounter by using "for" instead of ".each"
In the goto case - we use it all the time under different names with special circumstances: method calls, loops, breaks, nexts, etc.
In the case of for vs. each - it depends. In some cases you may indeed be iterating over the contents of some object in particular. But what if you are iterating over the contents of two disparate objects? Should we use ".each" on one and keep a separate counter for the other?
And then, there are those times when you actually want the variables in the loop to be available outside the scope of the loop, as I mentioned in the previous comment. It might not be standard, but it does happen. One instance is where you are letting doing evals on variables introduced by the objects in the loop. That just can't happen with the .each call, and you need the "for" loop to do it.
And I would add, even if it were possible to keep track of that in a block we pass to #each, I would rather not do so because of the psychological implications of doing so. If I want to iterate over two disparate objects, I need a way of expressing that, and the "for" loop is it.
a = 1
for a in 1..2
b = a
end
p a
>> 2
It seems natural that that would change the value of "a", I wouldn't expect a for loop to create a new scoping context. The example with putting a closure into the array is also what I would expect.I am not sure what programming language has the scoping rules he expects, but for me there's nothing to see here, moving along...
It's not the case where you set a beforehand that matters, but when you don't.
For example:
>> (1..2).each { |i| p i }
1
2
=> 1..2
>> i
NameError: undefined local variable or method `i'
>> for i in 1..2; p i; end
1
2
=> 1..2
>> i
=> 2
So the real danger comes in here: >> procs = []
=> []
>> (1..2).each { |i| procs << lambda { i } }
=> 1..2
>> procs.map { |e| e.call }
=> [1, 2]
>> procs2 = []
=> []
>> for i in 1..2; procs2 << lambda { i }; end
=> 1..2
>> procs2.map { |e| e.call }
=> [2, 2]
Which seems better to you?If you're not coming from Ruby, I can understand how reading the first part of this entry might make you think "what's the big deal", but if you read on, you'll see an extended version of what I've shown above.
It's the closures that make this issue complicated.
#assuming a is unbound at this point
for a in 1..2
b = 2
end
p a
>> 2
I expect "a" to be free outside the loop in Ruby. for my $a (1..2) {
$b = 2;
}
print $a;
Prints nothing. (Of course, with warnings or strict this complains about both $b and the second $a, as they're either never used or undefined.)Block scope is very nice. Maybe I don't use enough other OO languages to really grok why you'd want it any other way. What's the benefit to having method rather than block scope in such a context? I don't remember hating not having block scope in Python when I switched for about three or four years, but I didn't really use it in Perl at that point either, so I might miss it now that I do.
Edit: I mean in
each { |i| p i }
the |i| stands for a shorthand lambda, that binds an i.And in
for i in 1..3
p i
end
there is no such binding. So where's the problem? That 'for' should use a lambda?