Issue #7690 has been updated by shugo (Shugo Maeda). Status changed from Open to Assigned Assignee set to shugo (Shugo Maeda) marcandre (Marc-Andre Lafortune) wrote: > I would expect that > > array.flat_map{...} == array.lazy.flat_map{...}.force > > This is not always the case: > > [1].flat_map{|i| {i => i} } # => [{1 => 1}], ok > [1].lazy.flat_map{|i| {i => i} }.force # => [[1, 1]], expected [{1 => 1}] I agree that this looks weird. > Note that Matz confirmed that it is acceptable to return straight objects instead of arrays for flat_map [ruby-core:43365] > > It looks like this was intended for nested lazy enumerators: > > [1].lazy.flat_map{|i| [i].lazy }.force # => [1] > > I don't think that's the correct result, and it is different from a straight flat_map: > > [1].flat_map{|i| [i].lazy } # => [#<Enumerator::Lazy: [1]>] [1].lazy.flat_map{|i| [i].lazy } should flatten nested lazy enumerators, because Enumerable::Lazy is a monad and flat_map is the monad's bind operator. In the monad, [x].lazy is equivalent to Haskell's return and flat_map is equivalent to Haskell's >>= (bind). # return :: a -> ma [x].lazy # (>>=) :: m a -> (a -> m b) -> m b x.flat_map(&f) Note that f's type is a -> m b, which means that f returns not an Array, but an Enumerable::Lazy. In fact, [x].lazy and flat_map obey the monad laws. # (return x) >>= f == f x [x].lazy.flat_map(&f) == f.(x) # m >>= return == m m.flat_map { |i| [i].lazy } == m # (m >>= f) >>= g == m >>= (\x -> f x >>= g) m.flat_map(&f).flat_map(&g) == m.flat_map { |x| f.(x).flat_map(&g) } That is, flat_map is an operator to compose computations which return an Enumerable::Lazy. Do you have any use case of [1].flat_map{|i| {i => i} }? ---------------------------------------- Bug #7690: Enumerable::Lazy#flat_map should not call each https://bugs.ruby-lang.org/issues/7690#change-35388 Author: marcandre (Marc-Andre Lafortune) Status: Assigned Priority: Normal Assignee: shugo (Shugo Maeda) Category: core Target version: 2.0.0 ruby -v: r38794 I would expect that array.flat_map{...} == array.lazy.flat_map{...}.force This is not always the case: [1].flat_map{|i| {i => i} } # => [{1 => 1}], ok [1].lazy.flat_map{|i| {i => i} }.force # => [[1, 1]], expected [{1 => 1}] Note that Matz confirmed that it is acceptable to return straight objects instead of arrays for flat_map [ruby-core:43365] It looks like this was intended for nested lazy enumerators: [1].lazy.flat_map{|i| [i].lazy }.force # => [1] I don't think that's the correct result, and it is different from a straight flat_map: [1].flat_map{|i| [i].lazy } # => [#<Enumerator::Lazy: [1]>] This is caused by Lazy#flat_map calls each (while Enumerable#flat_map only looks for Arrays/object responding to to_ary). -- http://bugs.ruby-lang.org/