cplusplus / sender-receiver

Issues list for P2300
Apache License 2.0
20 stars 3 forks source link

Propose a `repeat[_n]` algorithm #14

Open ericniebler opened 10 months ago

ericniebler commented 10 months ago

Issue by ericniebler Tuesday Oct 25, 2022 at 16:28 GMT _Originally opened as https://github.com/brycelelbach/wg21_p2300_execution/issues/4_


These algorithms should come in two forms. Looking at repeat_n:

  1. repeat_n(loop_count, input_sender): sender of void
  2. repeat_n(predecessor_sender, loop_count, sender_adaptor_closure): sender of xvalues to values produced by the predecessor sender

Number 2. requires some explanation. It runs the predecessor sender once and caches the values. Then it executes something like repeat_n(loop_count, just-of-loop-index-and-cached-lvalues | sender_adaptor_closure), sending mutable lvalues of the cached values along with the current loop index through the value channel. After loop_count iterations, it sends xvalue references to the cached values to the downstream receiver.

Only the second form would support pipe syntax: predecessor | repeat_n(count, closure).

ericniebler commented 10 months ago

Comment by ericniebler Tuesday Oct 25, 2022 at 16:52 GMT


Additional thoughts: the downstream receiver's stop token should be checked at each iteration and the loop terminated if stop has been requested.

For the first algorithmic form, if at any iteration the input sender ends with an error or a stopped signal, the loop should stop executing and that signal propagated immediately.

For the second form, clearly the same behavior makes sense if the predecessor sender fails or is stopped, but what about the loop body (the closure)? Suggestion: if the loop body ends with an error, the loop stops executing and the error is propagated immediately. If the loop body ends with stopped then the loop body stops executing and one of two things happen:

  1. If the downstream receiver's stop token is in the stopped state, then stop is propagated downstream.
  2. Otherwise, the stopped signal should be treated like a break, and the cached values should be propagated through the value channel.

Since the second algorithmic form can exit the loop early and complete with success, it makes me think the number of times the loop was iterated should be propagated downstream along with the xvalue references to the cached values.

ericniebler commented 10 months ago

Comment by kirkshoop Wednesday Oct 26, 2022 at 14:48 GMT


It took me a bit to realize that these are defined without sequences and that explains the differences from the rx algorithms.

The first form works with sequences. Later we can add support for the set_next cpo to emit the result of each input sender completion without changing the returned sender from completing with void. With sequences it is a generator.

We will also need a repeat_n for input sequence senders (repeat_all_n?) that repeats the whole sequence and emits all the values in the sequence each time (instead of emitting the result of each input sender completion) as described above.

The second form is different. With sequences, the first form is composed with an index() algorithm to add an index to each set_next emitted and that is composed with then_each or let_each_value to do the closure and that is composed with last() to cache each value in the sequence and emit the cached value when the repeat_all_n completes.

Edited:

The second form would exist because it only starts the input sender once (so it repeats the results, not the side-effects) and because sequences are not in yet.

I am not sure that omitting the side-effects is something that should be combined into repeat. Perhaps the first form can do both.

repeat_n(10, let_value(tag(cache(input)), closure))

inbal2l commented 3 months ago

@lewissbaker @ericniebler marking as P1 as it's in P3118 (draft).