/// <summary> /// Merge a sequence of producers into a single producer /// </summary> /// <remarks>The merged producer completes when all component producers have completed</remarks> /// <param name="ms">Sequence of producers to merge</param> /// <returns>Merged producers</returns> public static Producer <RT, OUT, A> merge <RT, OUT, A>(Seq <Producer <RT, OUT, A> > ms) where RT : struct, HasCancel <RT> { return(from e in Producer.lift <RT, OUT, RT>(runtime <RT>()) from x in Producer.enumerate2 <RT, OUT>(go(e)) from _ in Producer.yield <RT, OUT>(x) select default(A)); async IAsyncEnumerable <OUT> go(RT env) { var queue = new ConcurrentQueue <OUT>(); var wait = new AutoResetEvent(true); var running = true; // Create a consumer that simply awaits a value and then puts it in our concurrent queue // to be re-yielded var enqueue = Consumer.awaiting <RT, OUT>() .Map(x => { queue.Enqueue(x); wait.Set(); return(default(A)); }) .ToConsumer(); // Compose the enqueue Consumer with the Producer to create an Effect that can be run var mme = ms.Map(m => m | enqueue).Strict(); // Run the producing effects // We should NOT be awaiting these var mmt = mme.Map(m => m.RunEffect().Run(env).AsTask()); // When all tasks are done, we're done // We should NOT be awaiting this #pragma warning disable CS4014 Task.WhenAll(mmt.ToArray()) .Iter(_ => { running = false; wait.Set(); }); #pragma warning restore CS4014 // Keep processing until we're cancelled or all of the Producers have stopped producing while (running && !env.CancellationToken.IsCancellationRequested) { await wait.WaitOneAsync(env.CancellationToken).ConfigureAwait(false); while (queue.TryDequeue(out var item)) { yield return(item); } } } }
/// <summary> /// Folds values coming down-stream, when the predicate returns true the folded value is yielded /// </summary> /// <param name="Initial">Initial state</param> /// <param name="Fold">Fold operation</param> /// <param name="UntilValue">Predicate</param> /// <returns>A pipe that folds</returns> public static Producer <RT, S, Unit> FoldWhile <RT, S, A>(this Producer <RT, S, A> ma, S Initial, Func <S, A, S> Fold, Func <S, bool> WhileState) where RT : struct, HasCancel <RT> { var state = Initial; return(ma.Bind( x => { state = Fold(state, x); if (WhileState(state)) { return Producer.Pure <RT, S, Unit>(unit); } else { var nstate = state; state = Initial; return Producer.yield <RT, S>(nstate); } })); }
/// <summary> /// Folds values coming down-stream, when the predicate returns true the folded value is yielded /// </summary> /// <param name="Initial">Initial state</param> /// <param name="Fold">Fold operation</param> /// <param name="UntilValue">Predicate</param> /// <returns>A pipe that folds</returns> public static Producer <RT, S, Unit> FoldUntil <RT, S, A>(this Producer <RT, S, A> ma, S Initial, Func <S, A, S> Fold, Func <A, bool> UntilValue) where RT : struct, HasCancel <RT> { var state = Initial; return(ma.Bind( x => { if (UntilValue(x)) { var nstate = state; state = Initial; return Producer.yield <RT, S>(nstate); } else { state = Fold(state, x); return Producer.Pure <RT, S, Unit>(unit); } })); }