Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can we say `arr.inject(:+)` but have to say `arr.map(&:to_s)`?

Tags:

ruby

It seems that we can always use &:+. Why can inject take :+ while map has to take &:to_s? If the reason is that map can't take a symbol but must take a block, then would it make sense if map takes a symbol too?

[1,3,5].inject(:+)   # => 9
[1,3,5].inject(&:+)  # => 9
[1,3,5].map(&:to_s)  # => ["1", "3", "5"]
[1,3,5].map(:to_s)   # => ArgumentError: wrong number of arguments (1 for 0)
like image 251
nonopolarity Avatar asked Feb 08 '23 04:02

nonopolarity


2 Answers

It is by design. inject allows an alternative syntax to take a symbol while map always requires a block.

Probably the reason symbol argument is allowed in inject but not in map is because inject always returns the resulting object of iteration, whereas map is designed to return an Enumerator instance when a block is absent. And in case the block is absent, it would become complicated if it had to see whether a symbol argument is provided in order to decide whether to return an Enumerator or the mapped Array instance. So, the Ruby designers made a simple rule for methods like map that the absence of a block means the return value would be an Enumerator.

Ironically, a disadvantage for inject to allow a symbol is that a symbol cannot be passed as the initial value in inject. But probably, the designers of Ruby decided that there would be rare use case for that.

like image 106
sawa Avatar answered Feb 23 '23 18:02

sawa


This is by design. Specifically, inject/reduce don't require the full symbol-to-proc syntax (&:+) but instead they allow to pass just a symbol (:+) (which a different behavior).

Please note that, in this specific case, passing a symbol is not the same of passing a proc derived from the symbol.

From the Ruby documentation

Combines all elements of enum by applying a binary operation, specified by a block or a symbol that names a method or operator.

If you specify a block, then for each element in enum the block is passed an accumulator value (memo) and the element. If you specify a symbol instead, then each element in the collection will be passed to the named method of memo. In either case, the result becomes the new value for memo. At the end of the iteration, the final value of memo is the return value for the method.

like image 44
Simone Carletti Avatar answered Feb 23 '23 19:02

Simone Carletti