Bug #6155
closedEnumerable::Lazy#flat_map raises an exception when an element does not respond to #each
Added by dkubb (Dan Kubb) over 13 years ago. Updated over 13 years ago.
Description
The following statement will raise "NoMethodError: undefined method `each' for 1:Fixnum":
[1, 2, 3].lazy.flat_map { |n| n }.to_a
It appears as if Enumerable::Lazy#flat_map is calling #each on every element, regardless of whether it can work or not.
As a reference, the equivalent statement using Enumerable#flat_map works:
[1, 2, 3].flat_map { |n| n }.to_a
Updated by shugo (Shugo Maeda) over 13 years ago
Actions
#1
[ruby-core:43336]
- Assignee set to shugo (Shugo Maeda)
Hello,
Dan Kubb wrote:
The following statement will raise "NoMethodError: undefined method `each' for 1:Fixnum":
[1, 2, 3].lazy.flat_map { |n| n }.to_a
It appears as if Enumerable::Lazy#flat_map is calling #each on every element, regardless of whether it can work or not.
As a reference, the equivalent statement using Enumerable#flat_map works:
[1, 2, 3].flat_map { |n| n }.to_a
I doubt that this behavior of Enumerable#flat_map is reasonable.
flat_map is designed to concatenate multiple collections into one collection, so in the above example, map should be used instead of flat_map.
[1, 2, 3].concat(4) raises a TypeError, so [1, 2, 3].flat_map { |n| n } should raise an error, I think.
Updated by shugo (Shugo Maeda) over 13 years ago
Actions
#2
[ruby-core:43337]
- Status changed from Open to Assigned
Updated by dkubb (Dan Kubb) over 13 years ago
Actions
#3
[1, 2, 3].flat_map { |n| n }.to_a
I doubt that this behavior of Enumerable#flat_map is reasonable.
I was writing rubyspec for Enumerable::Lazy#flat_map had the same behaviour as Enumerable#flat_map (besides the obvious differences in return value). Here is what I was using as a basis: https://github.com/rubyspec/rubyspec/blob/master/core/enumerable/shared/collect_concat.rb
The last example doesn't apply, since it should raise an exception when no block is provided (according to the current implementation), but the first 3 specs fail because they have mixed data, they are equivalent to:
[1, [2, 3], [4, [5, 6]], {:foo => :bar}].lazy.flat_map { |e| e }.to_a
[1, [], 2].lazy.flat_map { |e| e }.to_a
[[:foo], Object.new.tap { |o| class << o; def to_a() end end }].lazy.flat_map { |e| e }.to_a
All of the above examples work when the enumerable is not lazy.
flat_map is designed to concatenate multiple collections into one collection, so in the above example, map should be used instead of flat_map.
I was trying to demonstrate the simplest example that would cause an exception like the rubyspec examples but I probably wouldn't use it myself except for multiple collections.
Updated by marcandre (Marc-Andre Lafortune) over 13 years ago
Actions
#4
[ruby-core:43357]
- Category set to core
- Target version set to 2.0.0
Hi,
Shugo Maeda wrote:
I doubt that this behavior of Enumerable#flat_map is reasonable.
flat_map is designed to concatenate multiple collections into one collection, so in the above example, map should be used instead of flat_map.[1, 2, 3].concat(4) raises a TypeError, so [1, 2, 3].flat_map { |n| n } should raise an error, I think.
I understand your point of view. This behavior was clearly intended in Matz's original commit, though (r25456). Changing this could also introduce some compatibility issues.
I feel it might be best to keep the current behavior, but Matz will have the final word on this.
Updated by matz (Yukihiro Matsumoto) over 13 years ago
Actions
#5
[ruby-core:43365]
Hi,
In message "Re: [ruby-core:43357] [ruby-trunk - Bug #6155] Enumerable::Lazy#flat_map raises an exception when an element does not respond to #each"
on Sat, 17 Mar 2012 06:42:22 +0900, Marc-Andre Lafortune ruby-core@marc-andre.ca writes:
|> [1, 2, 3].concat(4) raises a TypeError, so [1, 2, 3].flat_map { |n| n } should raise an error, I think.
|
|I understand your point of view. This behavior was clearly intended in Matz's original commit, though (r25456). Changing this could also introduce some compatibility issues.
|
|I feel it might be best to keep the current behavior, but Matz will have the final word on this.
I vote on keeping the current behavior.
matz.
Updated by shugo (Shugo Maeda) over 13 years ago
Actions
#6
[ruby-core:43463]
I'll fix lazy flat_map respecting Matz's opinion, but let me clarify one point.
dkubb (Dan Kubb) wrote:
[1, 2, 3].flat_map { |n| n }.to_a
I doubt that this behavior of Enumerable#flat_map is reasonable.
I was writing rubyspec for Enumerable::Lazy#flat_map had the same behaviour as Enumerable#flat_map (besides the obvious differences in return value). Here is what I was using as a basis: https://github.com/rubyspec/rubyspec/blob/master/core/enumerable/shared/collect_concat.rb
The last example doesn't apply, since it should raise an exception when no block is provided (according to the current implementation), but the first 3 specs fail because they have mixed data, they are equivalent to:
[1, [2, 3], [4, [5, 6]], {:foo => :bar}].lazy.flat_map { |e| e }.to_a [1, [], 2].lazy.flat_map { |e| e }.to_a [[:foo], Object.new.tap { |o| class << o; def to_a() end end }].lazy.flat_map { |e| e }.to_a
All of the above examples work when the enumerable is not lazy.
Why the last example defines to_a? flat_map doesn't call to_a, but to_ary.
p [Object.new.tap { |o|
class << o
def to_a; [:to_a] end
end
}].flat_map { |e| e } #=> [#Object:0x21aae8a0]
p [Object.new.tap { |o|
class << o
def to_ary; [:to_ary] end
end
}].flat_map { |e| e } #=> [:to_ary]
Updated by shugo (Shugo Maeda) over 13 years ago
Actions
#7
- Status changed from Assigned to Closed
- % Done changed from 0 to 100
This issue was solved with changeset r35092.
Dan, thank you for reporting this issue.
Your contribution to Ruby is greatly appreciated.
May Ruby be with you.
- enumerator.c (lazy_flat_map_func): convert the block value to
Array if it doesn't respond to each. [ruby-core:43334]
[Bug #6155]
Updated by marcandre (Marc-Andre Lafortune) over 13 years ago
Actions
#8
[ruby-core:43478]
Hi,
shugo (Shugo Maeda) wrote:
Why the last example defines to_a? flat_map doesn't call to_a, but to_ary.
I think it's to spec that it doesn't call to_a
.
Updated by shugo (Shugo Maeda) over 13 years ago
Actions
#9
[ruby-core:43504]
Hello,
Ah, I see. RubySpec defines to_a to assert that it's never called, right?
That makes sense.
2012/03/20 2:27 "marcandre (Marc-Andre Lafortune)" <ruby-core@marc-andre.ca
:
Issue #6155 has been updated by marcandre (Marc-Andre Lafortune).
Hi,
shugo (Shugo Maeda) wrote:
Why the last example defines to_a? flat_map doesn't call to_a, but
to_ary.I think it's to spec that it doesn't call
to_a
.
Bug #6155: Enumerable::Lazy#flat_map raises an exception when an element
does not respond to #each
https://bugs.ruby-lang.org/issues/6155#change-24948Author: dkubb (Dan Kubb)
Status: Closed
Priority: Normal
Assignee: shugo (Shugo Maeda)
Category: core
Target version: 2.0.0
ruby -v: ruby 2.0.0dev (2012-03-15 trunk 35028) [x86_64-darwin11.3.0]The following statement will raise "NoMethodError: undefined method
`each' for 1:Fixnum":[1, 2, 3].lazy.flat_map { |n| n }.to_a
It appears as if Enumerable::Lazy#flat_map is calling #each on every
element, regardless of whether it can work or not.As a reference, the equivalent statement using Enumerable#flat_map works:
[1, 2, 3].flat_map { |n| n }.to_a