IntStreams and the case of the missing reduce

Zhong Yu zhong.j.yu at gmail.com
Tue Jan 7 06:17:29 PST 2014


Good points, but not enough to overcome the sense of dirtiness of the solution.

I guess for now applications should just live with boxed values and
don't worry about it, until it's proven to be a performance bottleneck
(then you optimize it with manual, low level, imperative code)

Zhong Yu



On Tue, Jan 7, 2014 at 6:09 AM, Brent Walker <brenthwalker at gmail.com> wrote:
> 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