I second that `Stream.append` would be a very useful addition, although I
would definitely propose it as
        Stream<T> append(Stream<? extends T> other) -> Stream.concat(this,
other);

Of course, we could also have a convenience overload like
        Stream<T> append(T... values) -> append(Arrays.stream(values));
but I do not find it nearly as important.


As for `Stream.prepend`, I find it quite non-intuitive because of the flow
inversion. For example, I *much* prefer
        var remainingStream = list.stream()
                .filter(...)
                .map(...);
        Stream.of(specialElement).append(remainingStream).collect(toList());
to
        list.stream()
                .filter(...)
                .map(...)
                .prepend(specialElement)
                .collect(toList());
even though the latter is shorter and it's a single expression.

Also, note how
        Stream.of(specialElement).append(remainingStream).collect(toList());
has better "flow" than
       Stream.concat(Stream.of(specialElement),
remainingStream).collect(toList());


Regards,
Tomasz Linkowski


On Mon, Oct 15, 2018 at 4:51 AM Tagir Valeev <amae...@gmail.com> wrote:

> Hello!
>
> Still from the practical point of view it would be really helpful to
> have instance methods like `Stream.append(T... elements)` and
> `Stream.prepend(T... elements)` (default implementation may utilize
> `concat`). Very often it's necessary to add one or two special
> elements to the stream, and using the `concat` makes code very ugly.
>
> C.f.:
>
>
> list.stream().filter(...).map(...).append(specialElement).collect(toList());
> v.s.:
> Stream.concat(list.stream().filter(...).map(...),
> Stream.of(specialElement)).collect(toList());
>
> Currently I prefer this (unless I can use third-party libraries which
> provide `append`):
> List<T> result = list.stream().filter(...).map(...).collect(toList());
> // toCollection(ArrayList::new) for purists
> result.add(specialElement);
>
> With best regards,
> Tagir Valeev.
> On Thu, Oct 11, 2018 at 11:11 PM Brian Goetz <brian.go...@oracle.com>
> wrote:
> >
> > It would surely be convenient; I've wished for this a few times. But,
> > there's a reason for this choice, and its not just laziness;
> > contravariant type variables have some pretty scary challenges. See, for
> > example, this paper:
> >
> >      http://www.cis.upenn.edu/~bcpierce/papers/variance.pdf
> >
> > in which it is shown that contravariance is one of the ingredients in a
> > witches brew that leads to undecideability of type checking.
> >
> >
> >
> > On 10/11/2018 12:06 AM, James Roper wrote:
> > > With the work I'm doing at the moment at creating a Reactive Streams
> > > equivalent to java.util.stream, I've often wondered why Stream.concat
> is a
> > > static method, rather than an instance method concating the given
> stream
> > > onto this. But I think the reason has just dawned on me, and I wanted
> to
> > > confirm that I'm correct.
> > >
> > > Java doesn't support contravariant type variables - it does for type
> > > declarations, but not type variables.
> > >
> > > To put more concretely, if I had a Stream<Integer>, and I wanted to
> concat
> > > a Stream<Number>, this is a valid thing to do, the resulting stream
> would
> > > be Stream<Number>. But doing that with an instance method would require
> > > something like this:
> > >
> > > public <S super T> Stream<S> concat(Stream<? extends S> b);
> > >
> > > Which is not supported (specifically, <S super T> type variable
> declaration
> > > is not supported). In contrast, what we have in the actual API:
> > >
> > > public static <T> Stream<T> concat(Stream<? extends T> a, Stream<?
> extends
> > > T> b);
> > >
> > > does allow me to concat a Stream<Integer> and Stream<Number> with a
> > > resulting type of Stream<Number>.
> > >
> > > Is this right, or are there other reasons? Also, is there any
> possibility
> > > that Java might support contravariance in type variables in future? My
> > > reason for wanting it is to provide the following method for reactive
> > > streams:
> > >
> > > public <S super T> Publisher<S> onErrorResumeWith(Function<? super
> > > Throwable, ? extends Publisher<? extends S>> f);
> > >
> > > The intent of this method is when a stream encounters an error, the
> passed
> > > function is invoked with the error, and that function returns a
> publisher
> > > that gets concated to the current stream instead of the error being
> > > emitted. This could possibly be implemented with a static method:
> > >
> > > public static <T> Publisher<T> onErrorResumeWith(Publisher<? extends
> T> a,
> > > Function<? super Throwable, ? extends Publisher<? extends T> f);
> > >
> > > But unlike concat, this method feels and reads much better as an
> instance
> > > method, as a static method it's a little confusing.
> > >
> > > Regards,
> > >
> > > James
> > >
> >
>


-- 
Pozdrawiam,
Tomasz Linkowski

Reply via email to