Bug #14015


Enumerable & Hash yielding arity

Added by marcandre (Marc-Andre Lafortune) over 3 years ago. Updated 7 months ago.

Target version:
ruby -v:
2.5.0 preview 1


The subtle difference between yield 1, 2 and yield [1, 2] has always confused me.

Today I wanted to pass a method to Hash#flat_map and realized how it's even more confusing than I thought.

I assumed that Hash#each was calling yield key, value. But somehow it's not that simple:

{a: 1}.map(&->(key, value){}) # => [nil]
{a: 1}.flat_map(&->(key, value){})  #=> ArgumentError: wrong number of arguments (given 1, expected 2)

What blows my mind, is that a custom method each that does yield a, 1 has different result!

class << o =
  include Enumerable
  def each
    yield :a, 1
end>(key, value){})  # => [nil]
o.flat_map(&->(key, value){})  # => [nil]  does not raise!!

I don't even know how that's possible, since Hash doesn't have a specialized flat_map method...

Here's a list of methods that accept a lambda of arity 2 (as I would expect)
For Hash
each, any?, map, select, reject,
For a custom yield
each, any?, map, count, find_index, flat_map, all?, one?, none?, take_while, uniq

These two lists have each, map and any? in common. Others work in one flavor, not the other. Many require arity 1: find, sort_by, grep, grep_v, count, detect, find_index, find_all, ...

To make things even more impossible, Hash#map has been working with arity 2 since Ruby 2.4 only.

Finally, Hash#each changes the expected arity of select, reject, and any?, but not of map:

    {a: 1}         .select(&->(a, b){})  # => {}
    {a: 1}>(a, b){}) # => wrong number of arguments (given 1, expected 2)


It seems more or less impossible to guess the expected arity of methods of Enumerable and of Hash, and they are not even consistent with one another. This makes these methods more or less unusable with lambdas.

While compatibility could be an issue, the fact that Hash#map has changed it's arity (I believe following ) makes me think that compatibility with the lesser used methods would be even less of a problem.

My personal wish: that the following methods be fixed to expect arity 2 for lambdas:

For both Hash & Enumerable:

  • find, sort_by, grep, grep_v, detect, find_all, partition, group_by, min_by, max_by, minmax_by, reverse_each, drop_while, sum For Hash:
  • count, find_index, flat_map, all?, one?, none?, take_while, uniq For Enumerable:
  • select, reject

Matz, what do you think?


yield_arity.rb (805 Bytes) yield_arity.rb marcandre (Marc-Andre Lafortune), 10/14/2017 08:25 PM

Also available in: Atom PDF