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
A
s
- 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
A
s
- 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.