javascalajava-streamvavr

What's the difference b/w foldLeft and Java collect


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:

and Java's collect:

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();


Solution

  • 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