Thanks for the JEP and your talk, Viktor! I think `Stream::gather` will be super useful in my day-to-day as a Java developer.

I’m curious why `fold` is being implemented with gatherers. I recognize `Gatherer` is designed to support intermediate operations, but `fold` feels inherently like a terminal operation to me since it, like `reduce` or `collect`, consumes all the elements in the stream and produces a single result.

Is there a technical limitation to making `fold` a terminal operation? For example, does `Collector` inherently presume parallelization in a way that `Gatherer` does not?

Or is the idea mainly to demonstrate the power of gatherers, and we could also make `fold` a terminal operation with the current `Stream` API?

Thank you!
Tyler Kindy

On Oct 27, 2023, at 9:50 AM, Viktor Klang <viktor.kl...@oracle.com> wrote:

Greetings,

As you may have already seen, Stream Gatherers is now a Preview JEP with Candidate status

Work-in-progress (interfaces, implementation, tests, benches, and documentation) for JEP-461 is currently available here.

While the design has held up well, there are some important improvements made since the original design document was written.

Notable changes (without any particular order):
  • Stream::gather() now has a default implementation.

  • Gatherer::supplier() was renamed to Gatherer::initializer() to better reflect intent.

  • Gatherer.Sink<R> was renamed to Gatherer.Downstream<R> to better signal what it represents.

  • Gatherer::collect(Collector) and its companion type Gatherer.ThenCollector was dropped due to compatibility concerns with existing code which operates on Collector.

  • Gatherer.Characteristics have been eliminated and superseded by having default values that are used as sentinels.
     (discussed further down the list)
    This is important because with the Characteristics-model keeping alignment between Characteristics and actual implementation proved brittle, and under composition of Gatherers computing the union was inefficient and mostly lead to an empty set in the end.

  • Gatherer.defaultInitializer(), Gatherer.defaultCombiner(), and Gatherer.defaultFinisher() were added as static methods—these are the sentinels used to elide calling the initializer, to elide calling the combiner (avoid parallelization), and to elide calling the finisher, respectively.

  • Gatherer::initializer(), Gatherer::combiner(), and Gatherer::finisher() default implementations return the respective sentinels.

  • A subtype of Integrator named Greedy was added, together with a factory method to guide lambda-to-nominal-type conversion. This allows creators of Gatherers to signal that an Integrator will never initiate a short-circuit (but may relay one from downstream), and that is available during evaluation to determine if the operation can short-circuit or not.

  • Factories for creating anonymous Gatherers were expanded upon to include Gatherer.of() and Gatherer.ofSequential() with different sets of parameters, primarily to make it more ergonomical and easier to read and write the code using those factories.

  • A curated set of initial built-in Gatherers is located in java.util.stream.Gatherers
I recently presented Gatherers at Devoxx, which I'd recommend watching for an introduction to the feature.
      

Cheers,


Viktor Klang
Software Architect, Java Platform Group
Oracle

Reply via email to