=begin pod

=TITLE class Supply

=SUBTITLE Asynchronous data stream with multiple subscribers

    class Supply { ... }

A supply is a thread-safe, asynchronous data stream like a
L<Channel|/type/Channel>, but it can have multiple subscribers
(I<taps>) that all get the same values flowing through the supply.

It is a thread-safe implementation of the
L<Observer Pattern|https://en.wikipedia.org/wiki/Observer_pattern>,
and central to supporting reactive programming in Perl 6.


There are two types of Supplies: C<live> and C<on demand>. When tapping into a
C<live> supply, the tap will only see values that are flowing through the
supply B<after> the tap has been created. Such supplies are normally infinite
in nature, such as mouse movements. Closing such a tap does not stop mouse
events from occurring, it just means that the values will go by unseen. All
tappers see the same flow of values.

A tap on an C<on demand> supply will initiate the production of values, and
tapping the supply again may result in a new set of values. For example,
C<Supply.interval> produces a fresh timer with the appropriate interval each
time it is tapped. If the tap is closed, the timer simply stops emitting values
to that tap.

A C<live> C<Supply> is obtained from the L<Supplier|/type/Supplier>
factory method C<Supply>.  New values are emitted by calling C<emit> on
the C<Supplier> object.

	my $supplier = Supplier.new;
	my $supply = $supplier.Supply;
	$supply.tap(-> $v { say "$v" });
	$supplier.emit(42); # Will cause the tap to output "42"

The L<live method|#method live> returns C<True> on live supplies. Factory
methods such as L<interval|#method interval>, L<from-list|#method from-list>
will return I<on demand> supplies.

=head1 Methods that return Taps

=head2 method tap

    method tap(Supply:D: &emit = -> $ { },
            :&done,
            :&quit={ die $_ },
            :&closing,
        ) returns Tap:D

Creates a new tap (a kind of subscription if you will), in addition to all
existing taps. The first positional argument is a piece of code that will be
called when a new value becomes available through the C<emit> call.

The C<&done> callback is called when the C<done> method on the supply is
called, indicating the end of life of the channel. For a C<live> supply the
C<done> method will be called on the parent C<Supplier>.

The C<&quit> callback is called when the C<quit> method on the supply is
called, indicating an erroneous termination of the supply. For a C<live>
supply the C<done> method will be called on the parent C<Supplier>

Method C<tap> returns an object of type L<Tap|/type/Tap>, on which you can
call the C<close> method to cancel the subscription.

    my $s = Supply.from-list(0 .. 5);
    my $t = $s.tap(-> $v { say $v }, done => { say "no more ticks" });

Produces:

    0
    1
    2
    3
    4
    5
    no more ticks

=head2 method act

    method act(Supply:D: &act) returns Tap:D

Creates a tap on the given supply with the given code.  Differently from
C<tap>, the given code is guaranteed to be only executed by one thread at
a time.

=head1 Utility methods

=head2 method Channel

    method Channel(Supply:D:) returns Channel:D

Returns a L<Channel|/type/Channel> object that will receive all future values
from the supply, and will be C<close>d when the Supply is done, and quit (shut
down with error) when the supply is quit.

=head2 method Promise

    method Promise(Supply:D:) returns Promise:D

Returns a L<Promise|/type/Promise> object for the next value. That means the
promise is kept when there is a next value, and broken when the supply is done
or quit before a value arrived.

    my $s = Supply.new;
    my $p = $s.Promise;
    $p.then(-> $v { say "got $v.result()" });
    $s.emit('cha');         # got cha

=head2 method close

    method close(Supply:D: Tap:D $t)

Closes the given tap, this is, calls its C<close> callback (if any), and
prevents it from getting any more values flowing through the supply.

=head2 method live

    method live(Supply:D:) returns Bool:D

Returns C<True> if the supply is "live", that is, values are emitted to taps
as soon as they arrive. Always returns C<True> in the default C<Supply> (but
for example on the supply returned from C<Supply.from-list> it's C<False>).

    say Supply.new.live;    # True

=head2 method schedule-on

    method schedule-on(Supply:D: $scheduler)

Runs the emit, done and quit callbacks on the specified scheduler.

This is useful for GUI toolkits that require certain actions to be run from
the GUI thread.

=head1 Methods that wait until the supply is done

=head2 method wait

    method wait(Supply:D:)

Waits until the supply is done (in which case it returns C<True>) or C<quit>
(in which case it will throw the exception that was passed to C<quit>).

    my $s = Supply.new;
    $s.tap(&say);
    my $p = Promise.allof((^10).pick(*).map: -> $v {
        start {
            $s.emit($v)
        }
    });
    $p.then({ $s.done });

    $s.wait;
    say "Done";
    await $p;

=head2 method list

    method list(Supply:D:) returns List:D

Waits until the supply is done, then returns a list of all values emitted
since the C<list> call.

=head2 method grab

    method grab(Supply:D: &process ) returns Supply:D

Waits until the given supply is done.  It will then call the given process
closure passing all of the values seen,  Then creates a supply to which all
values returned from the process closure.

=head2 method reverse

    method reverse(Supply:D:) returns Supply:D

Waits until the given supply is done, then creates a supply to which all
values seen are emitted in reverse order.

=head2 method sort

    method sort(Supply:D: &by?) returns Supply:D

Waits until the given supply is done, then creates a supply to which all
values seen are emitted in sorted order.  Optionally accepts a comparator
C<Block|/type/Block>.

=head1 Methods that return another Supply

=head2 method from-list

    method from-list(Supply:U: *@values) returns Supply:D

Creates an on-demand supply from the values passed to this method.

    my $s = Supply.from-list(1, 2, 3);
    $s.tap(&say);           # 1\n2\n3\n
    $s.done;

=head2 method share

    method share(Supply:D:) returns Supply:D

Creates a live supply from an on-demand supply, thus making it possible to
share the values of the on-demand supply on multiple taps, instead of each
tap seeing its own copy of all values from the on-demand supply.

    # this says in turn: "first 1" "first 2" "second 2" "first 3" "second 3"
    my $s = Supply.interval(1).share;
    $s.tap: { "first $_".say };
    sleep 1.1;
    $s.tap: { "second $_".say };
    sleep 2

=head2 method flat

    method flat(Supply:D:) returns Supply:D

Creates a supply on which all of the values seen in the given supply are
flattened before being emitted again.

=head2 method do

    method do(Supply:D: &do) returns Supply:D

Creates a supply to which all values seen in the given supply, are emitted
again.  The given code, executed for its side-effects only, is guaranteed
to be only executed by one thread at a time.

=head2 method interval

    method interval(Supply:U: $interval, $delay = 0) returns Supply:D

Creates a supply that emits a value every C<$interval> seconds, starting
C<$delay> seconds from the call. The value is an integer, starting from 0 at
the interval tock, and is incremented by one for each value emitted.

=head2 method grep

    method grep(Supply:D: Mu $test) returns Supply:D

Creates a new supply that only emits those values from the original supply
that smart-match against C<$test>.

    my $all  = Supply.new;
    my $ints = $all.grep(Int);
    $ints.tap(&say);
    $all.emit($_) for 1, 'a string', 3.14159;   # prints only 1

=head2 method map

    method map(Supply:D: &mapper) returns Supply:D

Returns a new supply that maps each value of the given supply through
C<&mapper> and emits it to the new supply.

    my $all    = Supply.new;
    my $double = $all.map(-> $value { $value * 2 });
    $double.tap(&say);
    $all.emit(4);           # 8

=head2 method batch

    method batch(Supply:D: :$elems, :$seconds) returns Supply:D

Creates a new supply that batches the values of the given supply by either
the number of elements in the batch (using :elems) or the maximum number of
seconds (using the :seconds) or both.  Any remaining values are emitted in
a final batch when the supply is done.

=head2 method elems

    method elems(Supply:D: $seconds?) returns Supply:D

Creates a new supply in which changes to the number of values seen are
emitted.  It optionally also takes an interval (in seconds) if you only want
to be updated every so many seconds.

=head2 method head

    method head(Supply:D: Int(Cool) $number = 1) returns Supply:D

Creates a "head" supply with the same semantics as List.head.

=head2 method tail

    method tail(Supply:D: Int(Cool) $number = 1) returns Supply:D

Creates a "tail" supply with the same semantics as List.tail.

=head2 method rotor

    method rotor(Supply:D: @cycle) returns Supply:D

Creates a "rotoring" supply with the same semantics as List.rotor.

=head2 method delayed

    method delayed(Supply:D: $seconds) returns Supply:D

Creates a new supply in which all values flowing through the given supply
are emitted, but with the given delay in seconds.

=head2 method throttle

    method throttle(Supply:D:
      $limit,                 # values / time or simultaneous processing
      $seconds or $callable,  # time-unit / code to process simultaneously
      $delay = 0,             # initial delay before starting, in seconds
      :$control,              # supply to emit control messages on (optional)
      :$status,               # supply to tap status messages from (optional)
      :$bleed,                # supply to bleed messages to (optional)
      :$vent-at,              # bleed when so many buffered (optional)
      :$scheduler,            # scheduler to use, default $*SCHEDULER
      ) returns Supply:D

Produces a C<Supply> from a given Supply, but makes sure the number of
messages passed through, is limited.

It has two modes of operation: per time-unit or by maximum number of
execution of a block of code: this is determined by the second positional
parameter.

The first positional parameter specifies the limit that should be applied.

If the second positional parameter is a C<Callable>, then the limit indicates
the maximum number of parallel processes executing the Callable, which is
given the value that was received.  The emitted values in this case will be
the C<Promise>s that were obtained from C<start>ing the Callable.

If the second positional parameter is a numeric value, it is interpreted as
the time-unit (in seconds).  If you specify B<.1> as the value, then it makes
sure you don't exceed the limit for every tenth of a second.
    
If the limit is exceeded, then incoming messages are buffered until there
is room to pass on / execute the Callable again.

The third positional parameter is optional: it indicates the number of
seconds the throttle will wait before passing on any values.

The :control named parameter optionally specifies a Supply that you can
use to control the throttle while it is in operation.  Messages that
can be sent, are strings in the form of "key:value".  Please see below
for the types of messages that you can send to control the throttle.

The :status named parameter optionally specifies a Supply that will receive
any status messages.  If specified, it will at least send one status message
after the original Supply is exhausted.  See L<status message> below.

The :bleed named parameter optionally specifies a Supply that will receive
any values that were either explicitly bled (with the B<bleed> control
message), or automatically bled (if there's a B<vent-at> active).

The :vent-at named parameter indicates the number of values that may be
buffered before any additional value will be routed to the :bleed Supply.
Defaults to 0 if not specified (causing no automatic bleeding to happen).
Only makes sense if a :bleed Supply has also been specified.

The :scheduler named parameter indicates the scheduler to be used.  Defaults
to $*SCHEDULER.

=head3 control messages

These messages can be sent to the :control Supply.  A control message
consists of a string of the form "key: value", e.g. "limit: 4".

=item limit

Change the number of messages (as initially given in the first positional)
to the value given.

=item bleed

Route the given number of buffered messages to the :bleed Supply.

=item vent-at

Change the maximum number of buffered values before automatic bleeding
takes place.  If the value is lower than before, will cause immediate
rerouting of buffered values to match the new maximum.

=item status

Send a status message to the :status Supply with the given id.

=head3 status message

The status return message is a hash with the following keys:

=item allowed

The current number of messages / callables that is still allowed to be
passed / executed.

=item bled

The number of messages routed to the :bleed Supply.

=item buffered

The number of messages currently buffered because of overflow.

=item emitted

The number of messages emitted (passed through).

=item id

The id of this status message (a monotonically increasing number).  Handy
if you want to log status messages.

=item limit

The current limit that is being applied.

=item vent-at

The maximum number of messages that may be buffered before they're
automatically re-routed to the :bleed Supply.

=head3 Examples

Have a simple piece of code announce when it starts running asynchronously,
wait a random amount of time, then announce when it is done.  Do this 6 times,
but don't let more than 3 of them run simultaneously.

  my $s = Supply.from-list(^6);  # set up supply 
  my $t = $s.throttle: 3,        # only allow 3 at a time
  {                              # code block to run
      say "running $_";          # announce we've started
      sleep rand;                # wait some random time
      say "done $_"              # announce we're done
  }                              # don't need ; because } at end of line
  $t.wait;                       # wait for the supply to be done
  =============
  running 0
  running 1
  running 2
  done 2
  running 3
  done 1
  running 4
  done 4
  running 5
  done 0
  done 3
  done 5

=head2 method stable

    method stable(Supply:D: $seconds, :$scheduler) returns Supply:D

Creates a new supply that only passes on a value flowing through the given
supply if it wasn't superseded by another value in the given time (in seconds).
Optionally uses another scheduler than the default scheduler, using the
:scheduler parameter.

=head2 method reduce

    method reduce(Supply:D: &with ) returns Supply:D

Creates a "reducing" supply with the same semantics as List.reduce.

=head2 method lines

    method lines(Supply:D: :$chomp = True ) returns Supply:D

Creates a supply that will emit the characters coming in line by line from a
supply that's usually created by some asynchronous I/O operation. The optional
:chomp parameter indicates whether to remove line separators: the default is
True.

=head2 method words

    method words(Supply:D:) returns Supply:D

Creates a supply that will emit the characters coming in word for word from a
supply that's usually created by some asynchronous I/O operation.

=head2 method unique

    method unique(Supply:D: :$as({$_}), :$with(&[===]), :$expires) returns Supply:D

Creates a supply that only provides unique values, as defined by the optional
:as and :with parameters (same as with List.unique).  The optional :expires
parameter how long to wait (in seconds) before "resetting" and not considering
a value to have been seen, even if it's the same as an old value.

=head2 method squish

    method squish(Supply:D: :$as({$_}), :$with(&[===]), :$expires) returns Supply:D

Creates a supply that only provides unique values, as defined by the optional
:as and :with parameters (same as with List.squish).  The optional :expires
parameter how long to wait (in seconds) before "resetting" and not considering
a value to have been seen, even if it's the same as an old value.

=head2 method max

    method max(Supply:D: &by = &infix:<cmp>) returns Supply:D

Creates a supply that only emits values from the given supply if they are
larger than any value seen before.  In other words, from a continuously
ascending supply it will emit all the values.  From a continuously descending
supply it will only emit the first value.  The optional parameter specifies
the comparator, just as with Any.max.

=head2 method min

    method min(Supply:D: &by = &infix:<cmp>) returns Supply:D

Creates a supply that only emits values from the given supply if they are
smaller than any value seen before.  In other words, from a continuously
descending supply it will emit all the values.  From a continuously ascending
supply it will only emit the first value.  The optional parameter specifies
the comparator, just as with Any.max.

=head2 method minmax

    method minmax(Supply:D: &by = &infix:<cmp>) returns Supply:D

Creates a supply that emits a Range every time a new minimum or maximum
values is seen from the given supply.  The optional parameter specifies
the comparator, just as with Any.max.

=head2 method start

    method start(Supply:D: &startee) returns Supply:D

Creates a supply of supplies. For each value in the original supply, the code
object is scheduled on another thread, and returns a supply either of a single
value (if the code succeeds), or one that quits without a value (if the code
fails).

This is useful for asynchronously starting work that you don't block on.

Use C<migrate> to join the values into a single supply again.

=head2 method migrate

    method migrate(Supply:D:) returns Supply:D

Creates a supply that acts like the last supply emitted to the given supply
of supplies (usually created with C<start>.

=head1 Methods that combine supplies

=head2 method merge

    method merge(Supply @*supplies) returns Supply:D

Creates a supply to which any value seen from the given supplies, is emitted.
The resulting supply is done Only when all given supplies are done.  Can also
be called as a class method.

=head2 method zip

    method zip(Supply @*supplies, :&with = &[,]) returns Supply:D

Creates a supply that emits combined values as soon as there is a new value
seen on B<all> of the supplies.  By default, C<Lists|/type/List> are
created, but this can be changed by specifying your own combiner with the
:with parameter.  The resulting supply is done as soon as B<any> of the given
supplies are done.  Can also be called as a class method.

=head2 method zip-latest

    method zip-latest(Supply @*supplies, :&with = &[,], :$initial) returns Supply:D

Creates a supply that emits combined values as soon as there is a new value
seen on B<any> of the supplies.  By default, C<Lists|/type/List> are
created, but this can be changed by specifying your own combiner with the
:with parameter.  The optional :initial parameter can be used to indicate
the initial state of the combined values.  By default, all supplies have to
have at least one value emitted on them before the first combined values is
emitted on the resulting supply.  The resulting supply is done as soon as
B<any> of the given supplies are done.  Can also be called as a class method.

=head1 I/O features exposed as supplies

=head2 sub signal

    sub signal(@*signals, :$scheduler);

Creates a supply for the Signal enums (such as SIGINT) specified, and an
optional :scheduler parameter.  Any signals received, will be emitted on
the supply.  For example:

    signal(SIGINT).tap( { say "Thank you for your attention"; exit 0 } );

would catch Control-C, thank you, and then exit.  The list of supported
signals can be found by checking Signal::.keys (as you would any enum).

=head2 method IO::Notification.watch-path

    method IO::Notification.watch-path($path) returns Supply:D

Creates a supply to which the OS will emit values to indicate changes on the
file system for the given path.  Also has a shortcut with the C<watch> method
on an IO object, like this:

    IO::Notification.watch-path(".").act( { say "$^file changed" } );
    ".".IO.watch.act(                     { say "$^file changed" } );   # same

=end pod
