Enumerator#each return value is surprising

Generally in Ruby, if enum is an Enumerable, enum.each returns enum –
this seems to be the case for all enums. But for Enumerator, it seems
to return…well, something else. For enumerators created with
enum_for, it returns the return value of the underlying function,
e.g.:

irb(main):065:0> e = [1,2,3].enum_for(:each)
=> #<Enumerator: [1, 2, 3]:each>
irb(main):066:0> e.each {}
=> [1, 2, 3]

While the return value of each is usually not that important (its
usually used as if it was a procedure rather than for its return
value), shouldn’t Enumerator#each return the receiver like Array#each,
Hash#each, etc.?

Hi,

ruby-1.9.1-p378 > e = [1,2,3].enum_for(:each)
=> #Enumerator:0x00000001577ce8
ruby-1.9.1-p378 > e.each {}
=> [1, 2, 3]
ruby-1.9.1-p378 > e.each {|i| p i}
1
2
3
=> [1, 2, 3]

I think it’s working just as one would expect it to…what you see as
the
return value at the end is ‘e’ itself.


Thanks & Regards,
Dhruva S…

On Sep 2, 2010, at 19:09 , Dhruva S. wrote:

=> [1, 2, 3]

I think it’s working just as one would expect it to…what you see as the
return value at the end is ‘e’ itself.

No, it isn’t. ‘e’ itself was output on the second line of your irb
session. What each is returning is what ‘e’ is wrapping up, the array.

That said, I think the result is as it should be, as enumerators should
be (in my mind at least) acting as stand-ins for the real thing, they
should be substitutable with the real thing and wind up with the same
result:

a = [1, 2, 3]
=> [1, 2, 3]

a.each {}
=> [1, 2, 3]

e = a.enum_for(:each)
=> #Enumerable::Enumerator:0x100370748

e.each {}
=> [1, 2, 3]

I think that is a nice amount of symmetry.

On Thu, Sep 2, 2010 at 7:57 PM, Ryan D. [email protected]
wrote:

That said, I think the result is as it should be, as enumerators should be (in my mind at least) acting as stand-ins for
the real thing, they should be substitutable with the real thing and wind up with the same result:

I suppose when the enumerator is created with the default options, I
can sort of see that (or at least, see it as unlikely to be harmful.)
When its created with enum_for with an argument other than :each,
though, it seems to me like you end up with more possibility for
unexpected results.

e.g.,

array = [“a”,“b”,“c”]
enum = array.enum_for(:each_with_index)
enum.each {} # => [“a”,“b”,“c”]
enum.to_a.each {} # => [[“a”, 0], [“b”, 1], [“c”, 2]]

That is, even ignoring the implementation class, the sequence returned
by Enumerator#each isn’t always the same sequence that the Enumerator
represents. This isn’t like any other Enumerable in core Ruby.

On Sep 3, 10:12 am, Christopher D. [email protected] wrote:

I suppose when the enumerator is created with the default options, I
can sort of see that (or at least, see it as unlikely to be harmful.)
When its created with enum_for with an argument other than :each,
though, it seems to me like you end up with more possibility for
unexpected results.

e.g.,

array = [“a”,“b”,“c”]
enum = array.enum_for(:each_with_index)
enum.each {} # => [“a”,“b”,“c”]
enum.to_a.each {} # => [[“a”, 0], [“b”, 1], [“c”, 2]]

That is, even ignoring the implementation class, the sequence returned
by Enumerator#each isn’t always the same sequence that the Enumerator
represents. This isn’t like any other Enumerable in core Ruby.

First system ruby 1.8.6, then rvm with 1.9.1

$ irb

%w[a b c].each {}
=> [“a”, “b”, “c”]
%w[a b c].each_with_index {}
=> [“a”, “b”, “c”]
exit

$ rvm use ruby-1.9.1
$ irb

%w[a b c].each
=> #Enumerator:0x48cc30
_.each {}
=> [“a”, “b”, “c”]
%w[a b c].each {}
=> [“a”, “b”, “c”]
%w[a b c].each_with_index {}
=> [“a”, “b”, “c”]
%w[a b c].each_with_index
=> #Enumerator:0x47f2f8
_.each {}
=> [“a”, “b”, “c”]

You should learn to expect the original array as a return value. It’s
not about what kind of each enumeration you use, but the ability to
chain calls as expected. If you want to get something different, use a
transforming enumerator instead of each.

On Fri, Sep 3, 2010 at 8:40 AM, Yossef M. [email protected]
wrote:

You should learn to expect the original array as a return value. It’s
not about what kind of each enumeration you use, but the ability to
chain calls as expected.

I think that is exactly what gets broken, though: Enumerator is the
only core Ruby class that mixes in Enumerable on which chained calls
to #each do not each get sent to the same receiver. To me, this seems
to be a leaky abstraction that adds a subtle potential complication to
code that operates on Enumerables.

If you want to get something different, use a
transforming enumerator instead of each.

I understand HOW the existing operations work. What I’m trying to
understand is the rationale, in part because I’m working on doing some
work on implementing some extensions to Enumerable to provide some a
few additional querying/joining operations over Ruby Enumerables
(vaguely like LINQ to Objects in .NET.) Naturally, this involves
implementing custom Enumerators. My inclination has always been that
custom Enumerables should behave like Ruby’s core enumerables (except,
I now realize, Enumerator) and return the receiver from #each for
consistency. Since the core enumerators don’t do that, though, I’d
like to avoid an implementation that is going to violate the
expectations of Enumerators.

Hi –

On Sat, 4 Sep 2010, Christopher D. wrote:

unexpected results.
represents. This isn’t like any other Enumerable in core Ruby.
I’d say that the enumerator represents enumeration logic (i.e., the
logic borrowed from its “host” method), rather than a particular
sequence. So you get the following parallels:

ruby-1.9.1-p378 > a = [1,2,3,4]
=> [1, 2, 3, 4]
ruby-1.9.1-p378 > e = a.each_with_index
=> #Enumerator:0x0000000af4dcb0

ruby-1.9.1-p378 > a.each_with_index.to_a
=> [[1, 0], [2, 1], [3, 2], [4, 3]]
ruby-1.9.1-p378 > e.to_a
=> [[1, 0], [2, 1], [3, 2], [4, 3]]

ruby-1.9.1-p378 > a.each_with_index.each
=> #Enumerator:0x0000000af0eb28
ruby-1.9.1-p378 > e.each
=> #Enumerator:0x0000000af4dcb0 # returns itself, since it’s
# already an enumerator
ruby-1.9.1-p378 > a.each_with_index.each {}
=> [1, 2, 3, 4]
ruby-1.9.1-p378 > e.each {}
=> [1, 2, 3, 4]

In other words, the enumerator is behaving like a.each_with_index, not
like a and not like itself.to_a. This isn’t surprising, of course, since
e is the return value from a.each_with_index :slight_smile: And I think it’s
consistent. In particular, note the last pair of examples, which both
return the original array object.

I guess I’m not sure how else it could work, since anything else would
(I think) be a departure from the idea that the enumerator gets its each
logic not just from a particular object but from an object/method
combination.

David


David A. Black, Senior Developer, Cyrus Innovation Inc.

The Ruby training with Black/Brown/McAnally
Compleat Philadelphia, PA, October 1-2, 2010
Rubyist http://www.compleatrubyist.com

On Tue, Sep 7, 2010 at 4:22 AM, David A. Black [email protected]
wrote:

I guess I’m not sure how else it could work, since anything else would
(I think) be a departure from the idea that the enumerator gets its each
logic not just from a particular object but from an object/method
combination.

I always really thought of Enumerators as representing a sequence with
the logic that produces the sequence being an implementation detail,
but I see that perspective. More importantly, I see the utility of
returning the underlying object in terms of making chained calls using
intermediate methods that produce an enumerator work like regular
iterator chaining.

I’ll need to think a bit about how that generalizes to some of the
custom enumerators I’m doing.