|
|
|
SearchCategories
|
The Evils of the For Loop
Posted about 1 year ago
in Early Steps and Ruby Tutorials.
I've never liked the That's mostly just my gut reaction, but if I had to put it into words it's that I fell in love with Ruby's iterators early on and First, let's see what I'm talking about. We are all pretty comfortable with
I doubt that surprises anyone. Many of you probably also know that Ruby allows you to write that as:
That's almost the same thing. It really does use
However, there's one tiny difference that ends up being pretty critical. The
It turns out that insignificant looking assignment makes all the difference. For those who aren't familiar with why it matters, have a look at this surprising Ruby 1.8 code:
Blocks in Ruby 1.8, like the one used by the Don't worry if the above behavior seems surprising and/or evil. It is. As a result, it was removed in Ruby 1.9. However, Using this knowledge, we can now see one often harmless side effect of using
Notice that
This is important so let me say it one more way. Calling
However, running the same code with an existing
Notice how we are missing a bunch of variable creation steps there? That's because there's only only variable now and it's not local to the block. I bet you are wondering why I've made such a big deal out of this tiny change by now. Why does this matter? Let me show you one more set of examples to answer that. First, this code shouldn't surprise you:
But does the following example surprise you? It sure surprised me the first time I saw it:
You should be able to reason it out now though. Remember, there's only one This example may seem a little contrived, but the truth is that it's not. I just simplified an actual bug report down to the core problem so I could explain it. Because we use blocks so much in Ruby, it's actually pretty easy to run into issues like this. The moral (my opinion, of course): |
|
|
|
Thanks for this post! I wonder how many times that bit me and I didn't even know it!!?
I don't use the for..in thinger, but even the fact that each() steps on my existing variables is probably cause for some of my hair-pulling. I'm ready for 1.9.
I notice you waited so very patiently for 1.9 to be right around the corner before you started bad-mouthing 1.8. :-)
I don't use the for..in thinger, but even the fact that each() steps on my existing variables is probably cause for some of my hair-pulling. I'm ready for 1.9.
You can actually use 1.9 to find these bugs in 1.8 code. Dave Thomas did exactly that recently while upgrading some code that runs in TextMate to be 1.9 friendly. He ran our scripts through 1.9, just to make sure they were working there. Because 1.9 has some new and improved warnings, it found bugs for us as an added bonus:
I notice you waited so very patiently for 1.9 to be right around the corner before you started bad-mouthing 1.8.
I really didn't mean it to be too negative. Consider it a sign of good things to come.
I adore Ruby and that includes 1.8, but I hope we all know it's not perfect in everything it does. ;)
I agree for loops are ugly, I hate to see them in plain Ruby code but have grown used to them in the Rails views.
Out of curiosity, if this problem with for..in stepping on local variables was fixed in 1.9, would you still have issues with using it?
Out of curiosity, if this problem with for..in stepping on local variables was fixed in 1.9, would you still have issues with using it?
I would still prefer
each().I feel it is better to stick with the uniform iterator approach throughout. For example, it's easier to turn
each()intoeach_with_index()thanfor. I also feel it's easier for us to just teach the iterator approach to new Rubyists if we don't bother with unneeded exceptions like theforloop.That's all just one guy's opinion though.
While my take is nowhere near as nuanced as yours, you can always refer to my version when you're feeling sassy: http://therealadam.com/archive/2008/01/06/the-barbarism-of-the-for-loop/
I think it's not a bug, it's feature. Consider the same code in other languages like C(++), PHP or Python - it works like the same.
A recent post by Andrej Bauer about a similar thing in Python:
http://math.andrej.com/2009/04/09/pythons-lambda-is-broken/
The discussion there, and the follow-up post:
http://math.andrej.com/2009/04/11/on-programming-language-design/
are interesting from the point of view of comparing how a language does work (from a comprehending-the-current-implementatino perspective) to how a language should work (from a general language design perspective).
for...in is easier to type and easier to read. You would only have problems with existing variables if your methods are too long.
That said, I use each() because I don't want the cool kids to sneer at me.