IntStreams and the case of the missing reduce
Paul Sandoz
paul.sandoz at oracle.com
Tue Jan 7 07:08:18 PST 2014
Hi Brent,
Here is an alternative way:
// assuming n -1 tails
// source is sequential but some parallelism can be extracted
return Stream.iterate(this, q -> q.tail()).skip(n).findFirst().get();
You want to use reduce as a form of fold-left functionality, which is inherently sequential and we did not add it for reasons you state. Perhaps that is a little extreme? i suppose i might prefer to see a foldLeft op than contortions of reduce, but there are potentially other solutions, as shown above.
A general solution is in the next revision of the API to add support for pluggable operations.
Paul.
On Jan 6, 2014, at 9:05 PM, Brent Walker <brenthwalker at gmail.com> wrote:
> I have been programming for a few months now in Java 8 and with streams in
> particular. I am implementing a persistent data structures library for
> Java. I have a couple of small criticisms to share with you guys. I know
> it's too late for Java 8 -- perhaps you can do something about them in a
> future release.
>
> I found (to my surprise) that one of the most useful classes that came with
> the lambda project is IntStream. Maybe it's my programming style, but I
> find myself using that class all the time. I rarely write explicit loops
> anymore. Unfortunately the most useful function (IMHO) in the stream
> class, the general form of the reduce function somehow did not make it to
> IntStream. I am talking about this function:
>
> <U> U reduce(U identity, BiFunction<U,? super T,U> accumulator,
> BinaryOperator<U> combiner);
>
> which in the IntStream case (had it existed) would be taking a
> BiIntFunction<U, U> accumulator.
>
> So for a function like the following:
>
> @Override
> public HMRealTimeQueue<V> drop(final int n) {
> HMRealTimeQueue<V> q = this;
> for (int i = 0; i != n; ++i) {
> q = q.tail();
> }
>
> return q;
> }
>
> I cannot replace it with the one liner:
>
> @Override
> public HMRealTimeQueue<V> drop(final int n) {
> return IntStream.range(0, cnt).reduce(this, (q, i) -> q.tail(),
> Functionals::functionShouldNotBeCalled);
> }
>
> because this version of the reduce is not there even though it is there on
> streams. I have to do:
>
> return IntStream.range(0, cnt).*boxed()*.reduce(this, (q, i) -> q.tail(),
> Functionals::functionShouldNotBeCalled);
>
> but the extra boxing is just silly. I honestly can't think of a good
> reason why you chose to omit it. Perhaps method/type creep? But we are
> talking about one method here and no extra types -- the reduce version for
> primitive to primitive I could live without.
>
> Another comment is the following: For the stream case the third argument
> to the reduce function is the combiner which is only used for parallel
> streams when pieces of the computation are combined. I applaud your
> efforts in designing an api that is agnostic to the kind of stream coming
> in and where parallelism can be turned on so easily with such minimal code
> changes. But it is maybe the case that in most uses of the reduce function
> (certainly that has been the case with me so far), the stream is sequential
> and could not be parallel (consider for instance the example above -- we
> cannot take the tail of a queue in parallel). In such cases I found I had
> to keep specifying something like:
>
> (z1, z2) -> { throw new AssertionFailure("This function should never be
> called. The stream was sequential"; }
>
> as the combiner which make the code, well ugly. Shortly after I started to
> use streams, I wrote this function in my utilities library.
>
> public static <A> A functionShouldNotBeCalled(final A a, final A b) {
> throw new AssertionError("Should never get here. The stream was
> sequential.");
> }
>
> and it is now in use in the majority of calls to reduce. You could perhaps
> have a version of reduce without the combiner so that we can avoid this
> code-smell in most uses of reduce -- if the stream happened to be parallel,
> then make do without the combiner -- do things sequentially for example.
>
> Apologies for the long email -- and thank you for your efforts in getting
> Java 8 finished -- Java is now a much more pleasant language to program in
> after the addition of lambdas and streams.
>
> Brent
>
More information about the lambda-dev
mailing list