IntStreams and the case of the missing reduce

Brent Walker brenthwalker at gmail.com
Tue Jan 7 04:09:11 PST 2014


Yes it would.  And because of one extra type we lost the symmetry between
the primitive and object streams, and lost the most useful reduce method
for raw streams.  I would call that the poorer choice.  The existence of
the distinction between primitives and objects in Java is annoying enough
as it is -- what's the point of exasperating it by building interfaces with
asymmetrys and missing methods for the primitives vs objects.

Once people start using Java 8, this will keep coming up.  A great number
of people will go through the "wait what, they left out a function?"
moment.  And unfortunately it is not just a moment; it keeps coming up as
you are coding, because that reduce() method is actually useful.

Brent



On Tue, Jan 7, 2014 at 9:42 AM, Zhong Yu <zhong.j.yu at gmail.com> wrote:

> That'll require a new specialized BiFunction type, and j.u.f package
> is already very crowded.
>
> On Mon, Jan 6, 2014 at 2: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