4

This question is targeted to Vavr (Java's FP library), but could probably apply to Scala or other FP languages that can compare to Java.

What's the difference between Vavr foldLeft:

  •  <U> U foldLeft(U zero,
                    BiFunction<? super U, ? super T, ? extends U> combiner)
    

and Java's collect:

  • <R> R collect(Supplier<R> supplier,
                  BiConsumer<R, ? super T> accumulator,
                  BiConsumer<R, R> combiner)
    

It seems both can achieve the same purpose: Transform a collection (say Stream<T>) by traversing it and accumulating a result into a new type U (or R) while traversing.

At first sight, signatures differ in #arguments, and Java collect seems to be intended to be run in parallel chunks.

Which are the actual differences conceptually? Vavr foldLeft seems easier to use.

Very dumb example just to illustrate:

Vavr foldLeft:

// result = "HelloFunctionalWorld"
String result = Seq("Hello", "Functional", "World")
    .foldLeft(new StringBuilder(), (acc, word) -> acc.append(word))
    .toString();

Java collect:

// result = "HelloFunctionalWorld"
final String result = Arrays.asList("Hello", "Functional", "World").stream()
    .collect(
        StringBuilder::new,
        (acc, word) -> acc.append(word),
        StringBuilder::append
    )
    .toString();

Gerard Bosch
  • 648
  • 1
  • 7
  • 18
  • Hi @NathanHughes, I think I should introduce the Java `reduce` operation in the question, as I can't either see the difference b/w `reduce` and `collect` for the particular dumb example. – Gerard Bosch Oct 23 '20 at 19:04
  • For what it's worth, I think factorials make for a better toy example here, e.g., https://scastie.scala-lang.org/ZxkWbgeRTaa0MbhrOGmG5Q – Alonso del Arte Oct 23 '20 at 19:37
  • @user, there is a version of `reduce` that reduces to a different type `U`, so the type of the stream and the type of the reduced value can be different. They're not required to be the same. – Gerard Bosch Oct 24 '20 at 00:12

1 Answers1

4

Which are the actual differences conceptually?

As the javadoc of collect(Supplier<R> supplier, BiConsumer<R,? super T> accumulator, BiConsumer<R,R> combiner) says:

Performs a mutable reduction operation on the elements of this stream. A mutable reduction is one in which the reduced value is a mutable result container, such as an ArrayList, and elements are incorporated by updating the state of the result rather than by replacing the result. This produces a result equivalent to:

R result = supplier.get();
for (T element : this stream)
    accumulator.accept(result, element);
return result;

The key phrase here is mutable, since Vavr doesn't do anything mutable, being a functional library.

The example in the question using StringBuilder is actually a violation of the functional principles. Why use Vavr if you don't intend to follow the functional paradigms?


The Java Stream equivalent of foldLeft() is reduce(), and you're correct, the 3rd parameter is to support parallel processing:

// Vavr
<U> U foldLeft​(U zero,
               BiFunction<? super U,​? super T,​? extends U> combine)

// Java Stream
<U> U reduce(U identity,
             BiFunction<U,? super T,U> accumulator,
             BinaryOperator<U> combiner)

If the result type is the same as the stream element type, you would be using the following alternatives, which don't need a 3rd parameter for parallel processing support:

// Vavr
T fold​(T zero,
       BiFunction<? super T,​? super T,​? extends T> combine)

// Java Stream
T reduce(T identity,
         BinaryOperator<T> accumulator)

UPDATE Comparison of the methods:

Vavr

Java Stream

Andreas
  • 154,647
  • 11
  • 152
  • 247
  • Thanks @andreas I see the point of immutability in the accumulator. Maybe I should change the original question to compare Vavr `foldLeft` with Java `reduce`. – Gerard Bosch Oct 23 '20 at 19:08
  • @GerardBosch Other than the 3rd parameter, there is no conceptual difference between `foldLeft`/`fold` and `reduce`, so what would your new question be asking? And please don't change the question to ask something different. If you want to ask something else, create a new question. – Andreas Oct 23 '20 at 19:11
  • 3
    _"Why use Vavr if you don't intend to follow the functional paradigms?"_ is really funny how people that do not understand functional programming believe that a simple, local, encapsulated mutation breaks any functional code. Building strings in the JVM is extremely expensive a local string builder is totally ok even on pure code. – Luis Miguel Mejía Suárez Oct 23 '20 at 19:11
  • @user In what way is `foldLeft` and `reduce` not the same, other than the 3rd parameter already mentioned in the answer? Yes, I agree that `foldRight` is slightly different, and I've never said otherwise, given that I never said anything about it. – Andreas Oct 23 '20 at 21:50
  • I think I get the subtle difference b/w `collect` and `reduce`: The mutable reduction, right? `collect` takes a `Biconsumer` (so mutates the accumulator), while `reduce` takes a `BiFunction` leading to a new accumulator instance each time (which makes sense in the context of persistent data structures for example). So finally, I guess the difference with `foldLeft` is that the later is not intended to be run in concurrent chunks and the order is guaranteed to be sequential. Is that correct? – Gerard Bosch Oct 23 '20 at 22:27
  • @user *"`reduce` doesn't guarantee order"* Sure it does. Why do you believe it doesn't? – Andreas Oct 23 '20 at 23:02
  • @user Why would subtraction work weird with `reduce`? If input is a stream with `1, 3, 7`, then result is `zero - 1 - 3 - 7` *(foldLeft)* or `identity - 1 - 3 - 7` *(reduce)*. Nothing weird there. With an appropriately written combiner method, `reduce` can even do it in parallel, which `foldLeft` can't. For *subtraction* operation, `zero`/`identify` is `0`, so the parallel result `combiner` is the *addition* operation: `reduce(0, (a,b) -> a - b, (a,b) -> a + b)` – Andreas Oct 23 '20 at 23:10
  • My bad. I didn't know the specification also talked about order being preserved. – user Oct 23 '20 at 23:43