=begin pod

=TITLE role Iterator

=SUBTITLE Generic API for producing a sequence of values

=for code :skip-test
constant IterationEnd
role Iterator { }

A C<Iterator> is an object that can generate or provide elements of a
sequence. Users usually don't have to care about iterators, their usage
is hidden behind iteration APIs such as C<for @list { }>, L<map>, L<grep>
and list indexing with C<.[$idx]>.

Iterators generally only allow one iteration over the sequence; afterwards
they are exhausted.

The main API is the C<pull-one> method, which either returns the next value,
or the sentinel value C<IterationEnd> if no more elements are available. Each
class implementing C<Iterator> must provide a C<pull-one> method. All other
Iterator API methods can be implemented in terms of C<pull-one>, but also
overridden for performance reasons.

Note: The only valid use of the sentinel value C<IterationEnd> in a program
is identity comparison (using C<=:=>) with the result of a method in the
iterator API. Any other behavior is undefined and implementation dependent.

=head1 Methods

=head2 method pull-one

Defined as:

    method pull-one(Iterator:D: --> Mu)

This method stub ensures that classes implementing the C<Iterator> role
provide a method named C<pull-one>.

The C<pull-one> method is supposed to return the next value if available, or
the sentinel value C<IterationEnd> if no more elements are available.

    my $i = (1 .. 3).iterator;
    say $i.pull-one; # OUTPUT: «1␤»
    say $i.pull-one; # OUTPUT: «2␤»
    say $i.pull-one; # OUTPUT: «3␤»
    dd $i.pull-one;  # IterationEnd

=head2 method push-exactly

Defined as:

    method push-exactly(Iterator:D: $target, int $count --> Mu)

Produces C<$count> elements, and for each of them, calls
C<$target.push($value)>.

If fewer than C<$count> elements are available from the iterator, it
returns the sentinel value C<IterationEnd>. Otherwise it returns
C<$count>.

    my @array;
    say (1 .. Inf).iterator.push-exactly(@array, 3); # OUTPUT: «3␤»
    say @array; # [1 2 3]

=head2 method push-at-least

Defined as:

    method push-at-least(Iterator:D: $target, int $count --> Mu)

Produces at least C<$count> elements, and for each of them, calls
C<$target.push($value)>.

If fewer than C<$count> elements are available from the iterator, it
returns the sentinel value C<IterationEnd>. Otherwise it returns
C<$count>.

Iterators with side effects should produce exactly C<$count> elements;
iterators without side effects (such as L<Range|/type/Range> iterators) can
produce more elements to achieve better performance.

    my @array;
    say (1 .. Inf).iterator.push-at-least(@array, 10); # OUTPUT: «10␤»
    say @array; # [1 2 3 4 5 6 7 8 9 10]

=head2 method push-all

Defined as:

    method push-all(Iterator:D: $target)

Produces all elements from the iterator and pushes them to C<$target>.

The fallback is implemented in terms of repeated C<push-at-least> with a large
C<$count>.

    my @array;
    say (1 .. 1000).iterator.push-all(@array); # All 1000 values are pushed

=head2 method push-until-lazy

Defined as:

    method push-until-lazy(Iterator:D: $target --> Mu)

Produces values until it considers itself to be lazy, and pushes them onto
C<$target>.

This matters mostly for iterators that have other iterators embedded, some of
which might be lazy, while others aren't.

=head2 method is-lazy

Defined as:

    method is-lazy(Iterator:D: --> Bool:D)

Returns C<True> for iterators that consider themselves lazy, and C<False>
otherwise.

Built-in operations that know that they can produce infinitely many values
return C<True> here, for example C<(1..6).roll(*)>.

    say (1 .. 100).is-lazy; # OUTPUT: «False␤»
    say (1 .. Inf).is-lazy; # OUTPUT: «True␤»

=head2 method sink-all

Defined as:

    method sink-all(Iterator:D:)

Exhausts the iterator (while discarding generated elements) purely for the
side effects of the iteration.

    say (1 .. 1000).iterator.sink-all;

=head2 method skip-one

Defined as:

    method skip-one(Iterator:D: $target --> Mu)

Skips one value. The return value is truthy if skip was successful and falsy if
there were no values to skip:

    my $i = <a b>.iterator;
    say $i.skip-one; say $i.pull-one; say $i.skip-one
    # OUTPUT: «1␤b␤0␤»

=head2 method skip-at-least

Defined as:

    method skip-at-least(Iterator:D: $target, int $to-skip --> Mu)

Skips C<$to-skip> values. The return value is truthy if skip was successful and falsy if
there were not enough values to skip:

    my $i = <a b c>.iterator;
    say $i.skip-at-least(2); say $i.pull-one; say $i.skip-at-least(20);
    # OUTPUT: «1␤c␤0␤»

=head2 method skip-at-least-pull-one

Defined as:

    method skip-at-least-pull-one(Iterator:D: $target, int $to-skip --> Mu)

Skips C<$to-skip> values and pulls the next value. The returns the pulled value
or C<IterationEnd> if there were not enough values:

    my $i = <a b c>.iterator;
    say $i.skip-at-least-pull-one(2);
    say $i.skip-at-least-pull-one(20) =:= IterationEnd;
    # OUTPUT: «c␤True␤»

=end pod
