While doing some casual web surfing I came across a rather interesting blog entry about Ruby’s types and looping. I started typing a reply, and then I realized it was really long, so I’m putting it here:
One reason I think methods like this are great is that Ruby is intended to be read! Which actually makes 5.times(&block) make much more sense than a C style 3 part for loop (that’s where it came from, not Java).
5.times do |i|; end
can be read as “5 times do this” or better “do this 5 times”
for on the other hand looks more like: for( i=0; i < 5; i++ ) {}
which has no such linear meaning, “for set i equal to 0, i less than 5, i plus one do this”, its all out of order. At best you can rearrange it in your head as “set i to 0, while i is less than 5 do do this, add one to i after each iteration”
for() is also prone to error with the condition operator, was that supposed to be < or <= ? Plenty of applications have had bugs because of this, and where something loops to isn’t always clear. If you really don’t like 5.times, there’s other methodologies though, in fact you can use a ‘for’ loop:
for i in (0...5); end
Read as “For i in the range of [0, 5) do this”
(0...5).each do |i|; end
Read as “for each in the range [0, 5) do this”
0.upto(4) do |i|; end
Again it reads linearly, from “from 0 up to 4 do this”.
I definitely think this makes Ruby more OO; Java suffers quite a lot from the distinction between primitive types and regular types. Want to design a collection in Java and store both objects and primitive types? You can’t declare:
class Collection<T> { ... public void add( T elem ); ... }
Instead you need to declare methods for each primitive type, and that method. This bloats Java types with many extra methods, or requires object wrappers that are a nasty hit on performance (something that’s totally unnecessary with a decent compiler). Other languages deal with this much more elegantly; by making numbers objects all you need to declare is the method that accepts the type T.
The Java API is greatly bloated because of this. Look at java.util.Arrays which has 9 methods to join() an array, one for each primitive type, and another for Object because of the Object#toString() and String.valueOf() distinction, even the String.valueOf() method has 9 signatures to deal with this limitation.
In ruby to get a string I can call to_s on *any* object, there is no null that causes exceptions, or special case primitives. I think Ruby unified all these types into a single Object hierarchy quite elegantly. :)
No tags