Reduce can be an override fold that doesn't take the first element. I guess there is an answer to that design decision but I can't find it.
CodePudding user response:
The two operations are fundamentally different. Giving them the same name would be confusing. In Ruby, the equivalent method is called Enumerable#inject
(aliased to Enumerable#reduce
) and is overloaded to act like both Scala's scala.collection.Iterable.foldLeft[B](z: B)(op: (B, A) => B): B
and scala.collection.Iterable.reduceLeft[B >: A](op: (B, A) => B): B
. And you can see the confusion this causes in ruby.
[Note: From here on, I will use Scala terminology to refer to the two different operations, i.e. fold
and reduce
.]
Some of the differences between the two operations are:
fold
works with an empty collection,reduce
doesn't. In Scala, it will either throw anUnsupportedOperationException
or you can usescala.collection.Iterable.reduceLeftOption[B >: A](op: (B, A) => B): Option[B]
. In Haskell, what Scala callsreduceLeft
is calledData.List.foldl1 :: Foldable t => (a -> a -> a) -> t a -> a
for that reason: because you need at least 1 element.- With
reduce
, the result type is the same as the element type, whereas withfold
, the result type can be anything. For example, you cannot sum the lengths of a collection of strings withreduce
because the result type ofreduce
on a collection of strings cannot be a number, it must be string. - The most important one:
fold
is a general iteration operation, meaning, everything you can do with a loop or with recursion, you can do with afold
. If you havefold
, you don't needmap
, you don't needfilter
, you don't needforall
, you don't needgroupBy
, you don't needscan
, and so on, and of course you also don't needreduce
. You can implement all of them usingfold
. (I did that for Ruby once.)reduce
is not general, and you can't e.g. implement any collection transformation because the result type ofreduce
is the element type and cannot be the collection type.
Because those two operations are so different, it just makes sense to give them different names: fold
and reduce
in Scala, foldX
and foldX1
in Haskell, etc. In the language I am most familiar with, Ruby, they have the same name, and that has led to confusion.
CodePudding user response:
In short, they have different type constraints, and therefore different type signatures. The result of a fold
can be anything you want. The result of a reduce
must be a supertype of the elements.
This is why fold
and reduce
are usually combined in dynamically-typed languages, but separated in statically-typed languages.