Just because you use a dynamically-typed language with implicit types, doesn't mean that you don't have to think about types.
In other languages, these two operations have different names (e.g. in Haskell foldl and foldl1, in Scala foldLeft and reduceLeft), whereas in Ruby, they are overloaded, but the matter still remains that they are two distinct operations:
the general fold operation
- operates on a collection of
As
- has a zero element of type
B
- and a combining function which takes a
B and an A and returns a B
which means
- it can return an object of a type other than the element type (it returns a
B, not an A)
- it can operate on an empty collection
the constrained reduce operation
- operates on a collection of
As
- has no zero element, it uses an element of the collection instead, which has type
A
- and a combining function which takes an
A and an A and returns an A
which means
- it can only return an object of the same type as the element type (it returns an
A)
- it can only operate on a non-empty collection
That's also where the Haskell names come from, it is called foldl1 because it requires at least 1 element, and takes element number 1 as the initial element.
Note that the general fold operation is strictly more powerful than the constrained reduce operation. In fact, the general fold operation is a universal iterator operation, it can do anything that a foreach loop can do: map, groupBy, sort, uniq, reverse, … you name it. Every collection operation can be implemented as a fold.
Example:
module Enumerable
def my_reverse
inject([]) {|acc, el| [el] + acc }
end
# this is not exactly equivalent because it's not lazy
def my_any?
inject(false) {|acc, el| acc || yield el }
end
end
reduce can not do everything. For example, you cannot implement reverse using reduce because reduce returns an A, but reverse returns a Collection<A>; likewise, you cannot implement any? because it returns a Boolean. You can only use reduce if these two properties hold:
- you want to produce a value of the same type as the element type
- there is at least one element
In your case, #1 is true, but #2 isn't, which is why you cannot use reduce, you must use fold. I.e. in Ruby, you need to use
ary.inject(0, :+)
and in ECMAScript
arr.reduce((acc, el) => acc + el, 0)
As mentioned elsewhere, Ruby does have Enumerable#sum; however, that only works for this specific case of summing the elements, but for example not for the practically identical problem of multiplying them.