Proposal: Stream.iterateWhile(T seed, Function<T, Optional<T>> mapper)

Tomasz Linkowski t.linkowski at gmail.com
Fri Oct 26 15:44:36 UTC 2018


Hi,

Please, consider adding a new static method to the `Stream` interface
(names TBD):

        static <T> Stream<T> iterateWhile(
            T seed, Function<? super T, ? extends Optional<? extends T>>
mapper
        );


== OVERVIEW ==

+ non-null equivalent of `Stream.iterate(seed, hasNext, next)` [1]
+ `mapper` like in `Optional.flatMap(mapper)` [2]
+ shift from two operations to one operation, like in:
    - `Iterator.hasNext/next` => `Spliterator.tryAdvance`
    - pattern matching (test/bind)
+ intent: "nudge towards writing clearer code" (Brian Goetz about LVTI [3])
+ useful for `Optional`-based APIs
+ trivial implementation


== JUSTIFICATION ==

`Stream.iterate(seed, hasNext, next)` is great for nullable-return-based
APIs. Example: returning a chain of `Throwable` causes:

        Stream.iterate(throwable, Objects::nonNull, Throwable::getCause)

For `Optional`-based APIs, using `Stream.iterate` becomes cumbersome.
Example (assume `Throwable.findCause()` returns `Optional`):

        Stream.iterate(throwable, Objects::nonNull, t ->
t.findCause().orElse(null))

Using the proposed method, the above can become much clearer:

        Stream.iterateWhile(throwable, Throwable::findCause)

This is just one example - I can provide more if needed.


== IMPLEMENTATION ==

Preferred implementation:

        iterateWhile(seed, mapper) -> Stream.iterate(
            seed, Objects::nonNull,
            t ->  mapper.apply(t).orElse(null)
        );

Equivalent `Optional`-based implementation:

        iterateWhile(seed, mapper) -> Stream.iterate(
                Optional.ofNullable(seed), Optional::isPresent,
                optional ->  optional.flatMap(mapper)
        ).map(Optional::get);

Note that both implementations assume that `null` seed yields an empty
`Stream`.


== NAMING ==

Name `iterate` cannot be safely overloaded because of
`Stream.iterate(UnaryOperator)` so another name needs to be used. I
proposed `iterateWhile` inspired by `takeWhile` but maybe it's a wrong
trail (`takeWhile` takes a `Predicate`). Other names that come to my mind:
`iterateWhilePresent`, `iterateOptional`, `iterateNonNull`.


[1]
https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/stream/Stream.html#iterate(T,java.util.function.Predicate,java.util.function.UnaryOperator)
[2]
https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Optional.html#flatMap(java.util.function.Function)
[3]
http://mail.openjdk.java.net/pipermail/amber-spec-experts/2018-October/000826.html

-- 
Regards,
Tomasz Linkowski


More information about the core-libs-dev mailing list