On 02/14/2016 07:15 PM, Stefan Zobel wrote:
Hi Tagir,

this looks good. I wonder, however, if the signature should
accept a wider range of types, i.e., something like


static <T, S extends T> Stream<T> iterate(S seed, Predicate<S>
predicate, UnaryOperator<S> f)


I know that the corresponding

static <T> Stream<T> iterate(T seed, UnaryOperator<T> f)

is less general than that, but I don't know whether this is
the outcome of a thoughtful decision or just an oversight.

What do you think?

What about even more permissive:

static <T, S extends T> Stream<T> iterate3(S seed, Predicate<? super S> predicate, Function<? super S, ? extends S> f)

...

public class SignatureTest {

static <T> Stream<T> iterate1(T seed, Predicate<T> predicate, UnaryOperator<T> f) {
        ...
    }

static <T, S extends T> Stream<T> iterate2(S seed, Predicate<S> predicate, UnaryOperator<S> f) {
        ...
    }

static <T, S extends T> Stream<T> iterate3(S seed, Predicate<? super S> predicate, Function<? super S, ? extends S> f) {
        ...
    }


    public static void main(String[] args) {

        Stream<Integer> si1 = iterate1(0, i -> i < 10, i -> i + 1); // OK
        Stream<Integer> si2 = iterate2(0, i -> i < 10, i -> i + 1); // OK
        Stream<Integer> si3 = iterate3(0, i -> i < 10, i -> i + 1); // OK

        Stream<Number> sn1 = iterate1(0, i -> i < 10, i -> i + 1);

//SignatureTest.java:32: error: bad operand types for binary operator '<' // Stream<Number> sn1 = iterate1(0, i -> i < 10, i -> i + 1);
        //                                                ^
//SignatureTest.java:32: error: bad operand types for binary operator '+' // Stream<Number> sn1 = iterate1(0, i -> i < 10, i -> i + 1);
//                                                             ^


        Stream<Number> sn2 = iterate2(0, i -> i < 10, i -> i + 1); // OK
        Stream<Number> sn3 = iterate3(0, i -> i < 10, i -> i + 1); // OK


        Predicate<CharSequence> csNotEmpty = cs -> cs.length() > 0;

Stream<CharSequence> css1 = iterate1("abcde", csNotEmpty, (String s) -> s.substring(1));

//SignatureTest.java:44: error: method iterate1 in class SignatureTest cannot be applied to given types; // Stream<CharSequence> css1 = iterate1("abcde", csNotEmpty, (String s) -> s.substring(1));
        //                                    ^
        //  required: T,Predicate<T>,UnaryOperator<T>
        //  found: String,Predicate<CharSequence>,(String s)[...]ng(1)
// reason: inference variable T has incompatible equality constraints String,CharSequence
        //  where T is a type-variable:
// T extends Object declared in method <T>iterate1(T,Predicate<T>,UnaryOperator<T>)


Stream<CharSequence> css2 = iterate2("abcde", csNotEmpty, (String s) -> s.substring(1));

//SignatureTest.java:54: error: method iterate2 in class SignatureTest cannot be applied to given types; // Stream<CharSequence> css2 = iterate2("abcde", csNotEmpty, (String s) -> s.substring(1));
        //                                    ^
        //  required: S,Predicate<S>,UnaryOperator<S>
        //  found: String,Predicate<CharSequence>,(String s)[...]ng(1)
// reason: inference variable S has incompatible equality constraints String,CharSequence
        //  where S,T are type-variables:
// S extends T declared in method <T,S>iterate2(S,Predicate<S>,UnaryOperator<S>) // T extends Object declared in method <T,S>iterate2(S,Predicate<S>,UnaryOperator<S>)


Stream<CharSequence> css3 = iterate3("abcde", csNotEmpty, (String s) -> s.substring(1)); // OK
    }
}


Regards, Peter


Regards,
Stefan


2016-02-14 15:53 GMT+01:00 Tagir F. Valeev <amae...@gmail.com>:
Hello!

I wanted to work on foldLeft, but Brian asked me to take this issue
instead. So here's webrev:
http://cr.openjdk.java.net/~tvaleev/webrev/8072727/r1/

I don't like iterator-based Stream source implementations, so I made
them AbstractSpliterator-based. I also implemented manually
forEachRemaining as, I believe, this improves the performance in
non-short-circuiting cases.

I also decided to keep two flags (started and finished) to track the
state. Currently existing implementation of infinite iterate() does
not use started flag, but instead reads one element ahead for
primitive streams. This seems wrong to me and may even lead to
unexpected exceptions (*). I could get rid of "started" flag for
Stream.iterate() using Streams.NONE, but this would make object
implementation different from primitive implementations. It would also
be possible to keep single three-state variable (byte or int,
NOT_STARTED, STARTED, FINISHED), but I doubt that this would improve
the performance or footprint. Having two flags looks more readable to
me.

Currently existing two-arg iterate methods can now be expressed as a
partial case of the new method:

public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) {
     return iterate(seed, x -> true, f);
}
(same for primitive streams). I may do this if you think it's
reasonable.

I created new test class and added new iterate sources to existing
data providers.

Please review and sponsor!

With best regards,
Tagir Valeev.

(*) Consider the following code:

int[] data = {1,2,3,4,-1};
IntStream.iterate(0, x -> data[x])
          .takeWhile(x -> x >= 0)
          .forEach(System.out::println);

Currently this unexpectedly throws an AIOOBE, because
IntStream.iterate unnecessarily tries to read one element ahead.


Reply via email to