On Saturday 05 December 2009 11:40:54 am Raul Jara wrote:
> Under ruby 1.8.6, running
> 
> [1, 2, 3].collect
> 
> results in an array of [1, 2, 3].

Huh. That's odd.

> [1, 2, 3].collect
> 
> results in an Enumerator, which I then have to call .to_a on.

Given that the original is an array, you probably wouldn't actually do this. 
Let's see...

> I was just wondering if
> anyone had any insight into why that change was made, because it feels
> very counter intuitive to me.

I agree with Rick -- the basic idea here is that all enumerator methods will 
turn into Enumerators when called without a block. I especially like doing it 
with variants of each.

> I really liked the elegance of opening
> up a file and just calling collect to turn it into an array.

An array of what? I wouldn't call that elegant, I'd call that confusingly 
magical. I think this is much easier to understand:

lines = open('foo') {|file| file.each_line.to_a}

Now I know exactly what's happening -- make an array of each line in the file. 
You could argue that this should be the default, but if you just did 'each', 
it's not obvious. I could, after all, be doing something like this:

chars = open('foo') {|file| file.each_char.to_a}

Or, if it's a binary file, something like this:

bytes = open('foo') {|file| file.each_byte.to_a}

You could also argue that the to_a should be unnecessary, but I don't agree. 
For one, it's really very useful to have that kind of shorthand for an 
Enumerator, for those few times it makes sense. For example:

open 'foo' do |file|
  lines = file.each_line
  begin
    loop do
      line = lines.next.chomp
      while line =~ /\\$/
        line = line.chop + lines.next.chomp
      end
      # do something with each continued line
    end
  rescue StopIteration
  end
end

I'm sure I could come up with other examples, too, including silly ones like:

(1..10).each_cons(2).map{|a,b| a*b}

Basically, I'm trying to say that Enumerators are cool -- I can think of all 
kinds of crazy uses for them, including one-liners like the above that rely on 
this behavior. I can't really think of a good reason for them to return arrays 
-- after all, you can always do a to_a at the end of a chain like that, if you 
actually need an array.

And anyway, if it's just something that someone will be calling 'each' on at 
some point, you probably don't need an array, and it's more efficient to use 
an enumerator anyway. After all, here's another contrived example:

byte_pairs = open('foo') {|file| file.each_char.each_slice(2).to_a }
line_pairs = open('foo') {|file| file.each_line.each_slice(2).to_a }

Both would work if each_char/each_line made an array, but the above is much 
more efficient.