5

We have an Enumerator::Lazy object

a = [1,2,3].lazy.map {} #=> <Enumerator::Lazy: #<Enumerator::Lazy: [1, 2, 3]>:map>
a.size #=> 3
a.clone.size #=> nil

Does anyone have correct explanation of such behaviour ? I know that size returns size of the enumerator, or nil if it can’t be calculated lazily. When we clone object it returns

a.clone #=> <Enumerator::Lazy:<Enumerator::Generator:0x00007fdaa80218d8>:each>
  • 1
    I'm not entirely sure what's going on here, but I would hazard a guess that it's got something to do with the **shallow** copy that `Object#clone` performs. I don't think it's even *possible* to perform a deep copy on a lazy enumerator (in general), and it's this nesting of enumerators that is causing the unusual behaviour. – Tom Lord Nov 30 '17 at 17:12
  • Perhaps this should be considered an (obscure) bug in ruby; I'm not sure. Maybe raise the issue on https://bugs.ruby-lang.org/ and see what they have to say about it? – Tom Lord Nov 30 '17 at 17:14
  • seems like it's been instroduced in recent versions, in 2.3.1 works as expected – Said Kaldybaev Nov 30 '17 at 18:08
  • Readers: if it's not evident, "I know that size returns size of the enumerator, or nil if it can’t be calculated lazily." is from the doc for [Enumerator#size](http://ruby-doc.org/core-2.4.0/Enumerator.html#method-i-size). – Cary Swoveland Nov 30 '17 at 19:51
  • There is a related thread [here](https://stackoverflow.com/questions/28839037/why-does-enumerable-not-have-a-length-attribute-in-ruby), which may be of interest. – Cary Swoveland Nov 30 '17 at 19:57

1 Answers1

3

I know that size returns size of the enumerator, or nil if it can’t be calculated lazily.

size for a Enumerator is not necessarily a real thing ( or atleast not what one would think it is) which may be the reason this change was implemented.

For example

[1,2,3].to_enum.size
#=> nil

[1,2,3].to_enum(:each) { 1 }.size #=> 1
#=> 1

or better still

[1,2,3].to_enum(:each) { "A" }.size 
#=> "A" 

When we clone object it returns a.clone #=> <Enumerator::Lazy<Enumerator::Generator:0x00007fdaa80218d8>:each>

This seems to be a change in 2.4 where it appears that it reverts back to the :each method (possibly through enum_for) because in 2.3 the reference to map is retained as is the size. Obviously due to the reversion no iteration has occurred and size cannot be determine in a "lazy" fashion and thus nil

engineersmnky
  • 25,495
  • 2
  • 36
  • 52
  • So according to your examples, it's a ruby bug (as `size` must return only real object size or `nil`). Mb you open an issue at https://bugs.ruby-lang.org ? – Alex Frolov Dec 01 '17 at 22:25
  • @AlexFrolov that is not what the docs state "The optional parameter can be used to specify how to calculate the size in a lazy fashion (see #size). It can either be a value or a callable object." it is up to you to determine what lazy calculation means – engineersmnky Dec 01 '17 at 22:43
  • I got that. Thank u for explanation – Alex Frolov Dec 01 '17 at 23:20