=begin pod :kind("Type") :subkind("class") :category("composite")

=TITLE class Seq

=SUBTITLE An iterable, potentially lazy sequence of values

    class Seq is Cool does Iterable does Sequence { }

A C<Seq> represents anything that can produce a sequence of values. A
C<Seq> is born in a state where iterating it will consume the values.
Calling C<.cache> on a C<Seq> will make it store the generated values
for later access.

A high-level construct to generate a C<Seq> is L«C<gather/take>|/syntax/gather%20take»,
as well as many built-in methods like L«C<map>|/routine/map» and
L«C<grep>|/routine/grep», low-level constructors to create a
C<Seq> from an iterator or from looping constructs are available too.

A C<Seq> can also be constructed with the
L«sequence operator C<...>|/language/operators#infix_...» or one of its
variants.

    my $s = (1...5);
    say $s;              # OUTPUT: «(1 2 3 4 5)␤»
    say $s.^name;        # OUTPUT: «Seq␤»

Assigning the values of a C<Seq> to an array consumes a C<Seq> that is
not lazy. Use the C<lazy> statement prefix to avoid a C<Seq> from being
iterated during the assignment:

=begin code
# The Seq created by gather ... take is consumed on the spot here.
my @a = gather do { say 'consuming...'; take 'one' };  # OUTPUT: «consuming...␤»

# The Seq here is only consumed as we iterate over @a later.
my @a = lazy gather do { say 'consuming...'; take 'one' };  # outputs nothing.
.say for @a;  # OUTPUT: «consuming...␤one␤»
=end code

A typical use case is L<method C<lines> in C<IO::Handle>|/type/IO::Handle#routine_lines>,
which could use a lot of memory if it stored all the lines read from the
file. So

=begin code
for open('README.md').lines -> $line {
    say $line;
}
=end code

won't keep all lines from the file in memory.

This implies that you cannot iterate the same C<Seq> object twice (otherwise
it couldn't throw away old values), so this dies:

    my @a = 1, 2, 3;
    my @b = <a b c>;
    my \c = @a Z=> @b;
    .say for c;
    .say for c; # fails
    CATCH { default { put .^name, ': ', .Str } };
    # OUTPUT: «X::Seq::Consumed: This Seq has already been iterated, and its values consumed
    # (you might solve this by adding .cache on usages of the Seq, or
    # by assigning the Seq into an array)»


B<Caution:> No program should ever assume a C<Seq> may only be iterated once
even if not cached by the program. Caching is a volatile state exposed to the
developer as an optimization. The C<Seq> may become cached by many operations,
including calling C<.raku> (C<.perl> before version 2019.11) on the C<Seq> (if
called prior to a non-cached iteration). From version 6.d, C<.raku> (again,
C<.perl> before version 2019.11) can be called on consumed C<Seq>. If a program
assumes a C<Seq> can only iterate once, but then is later changed to call one of
these operations during the loop, that assumption will fail.

On a cached C<Seq>, the cached list is used when C<&infix:<eqv>>, C<.Slip>,
C<.join>, C<.List>, C<.list>, C<.eager>, C<.Array> and C<.is-lazy> are called.

You can smartmatch a regex with C<Seq>, even if it's infinite

    my @fib = 1,1, *+* ... *;
    say @fib[^1000] ~~ /^9999/; # OUTPUT: «Nil␤»

However, infinite or lazy C<Seq> will be vivified when doing the match,
leading to possibly infinite loops, so be sure to limit search somehow.

=head1 Methods

=head2 method new

=for code
proto method new(Seq: |) {*}
multi method new(Seq: Iterator:D $iter)
multi method new(Seq: CachedIterator:D $iter)
multi method new(Seq:)

Creates a new C<Seq> object from the iterator (including a cached one) passed
as the single argument. Creates an empty C<Seq> if called with no argument

=head2 method iterator

    method iterator(Seq:D: --> Iterator:D)

If the C<Seq> is not cached, returns the underlying iterator and marks
the invocant as consumed. If called on an already consumed sequence,
throws an error of type L<X::Seq::Consumed|/type/X::Seq::Consumed>.

Otherwise returns an iterator over the cached list.

=head2 method is-lazy

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

Returns C<True> if and only if the underlying iterator or cached list
considers itself lazy. If called on an already consumed sequence, throws
an error of type L<X::Seq::Consumed|/type/X::Seq::Consumed>.

=head2 method elems

    method elems(Seq:D:)

Returns the number of values in the sequence. If this number cannot be
predicted, the C<Seq> is cached and evaluated till the end.

Because an infinite sequence cannot be evaluated till the end, such a
sequence I<should> be declared lazy. Calling C<.elems> on a lazy C<Seq>
L<fails|/routine/fail> with L<X::Cannot::Lazy|/type/X::Cannot::Lazy>.

=head2 method from-loop

    multi method from-loop(&body --> Seq:D)
    multi method from-loop(&body, &cond, :$repeat --> Seq:D)
    multi method from-loop(&body, &cond, &afterward --> Seq:D)

These methods create new C<Seq>-based callbacks.

The first form produces an infinite C<Seq> by calling C<&body> each time a new
element is requested, using the return value from C<&body> as the item. This
emulates (or implements) a C<loop { body }> construct.

The second form calls C<&cond> before each call to C<&body>, and terminates
the sequence if C<&cond> returns a false value. If C<$repeat> is set to a true
value, the first call to C<&cond> is omitted, and C<&body> called right away.
This emulates (or implements) C<while cond { body }> and
C<repeat { body } while cond> loops.

The third form enables C-style looping by calling a third callback,
C<&afterward>, after each call to C<&body>.

=head2 method sink

Defined as:

    method sink(--> Nil)

Calls L<C<sink-all>|/routine/sink-all> if it is an C<Iterator>, C<sink> if the Sequence is a list.

    say (1 ... 1000).sink; # OUTPUT: «Nil␤»

This is something you might want to do for the side effects of producing those values.

=head2 method skip

Defined as:

    multi method skip(Int() $n = 1 --> Seq)

Returns a Seq containing whatever is left of the invocant after
throwing away C<$n> of the next available values. Negative values of
C<$n> count as 0.  Also can take a WhateverCode to indicate how many values
to skip from the end.  Will block on lazy Seqs until the requested
number of values have been discarded.

    say (1..5).map({$_}).skip;      # OUTPUT: «(2,3,4,5)␤»
    say (1..5).map({$_}).skip(3);   # OUTPUT: «(4,5)␤»
    say (1..5).map({$_}).skip(5);   # OUTPUT: «()␤»
    say (1..5).map({$_}).skip(-1);  # OUTPUT: «(1,2,3,4,5)␤»
    say (1..5).map({$_}).skip(*-3); # OUTPUT: «(3,4,5)␤»

=end pod

# vim: expandtab softtabstop=4 shiftwidth=4 ft=perl6
