/// <summary> /// Unwrap a stream inside a behavior to give a time-varying stream implementation. /// When the behavior changes value, the output stream will fire the simultaneous firing (if one exists) from the stream /// which the behavior held at the beginning of the transaction. /// </summary> /// <typeparam name="T">The type of the stream.</typeparam> /// <param name="bsa">The behavior containing the stream.</param> /// <returns>The unwrapped stream.</returns> public static Stream <T> SwitchS <T>(this Behavior <Stream <T> > bsa) { return(Transaction.Apply( (trans1, _) => { Stream <T> @out = new Stream <T>(bsa.KeepListenersAlive); MutableListener currentListener = new MutableListener(); void HInitial(Transaction trans2, Stream <T> sa) { currentListener.Unlisten(); currentListener.SetListener(sa.Listen(@out.Node, trans2, @out.Send, false)); } void H(Transaction trans2, Stream <T> sa) { trans2.Last( () => { currentListener.Unlisten(); currentListener.SetListener(sa.Listen(@out.Node, trans2, @out.Send, true)); }); } trans1.Prioritized(new Node <T>(), trans2 => HInitial(trans2, bsa.SampleNoTransaction())); IListener l1 = bsa.Updates().Listen(new Node <T>(), trans1, H, false); return @out.UnsafeAttachListener(l1).UnsafeAttachListener(currentListener); }, false)); }
private static Event <TA> SwitchE <TA>(Transaction trans1, Behavior <Event <TA> > bea) { var @out = new EventSink <TA>(); var h2 = new TransactionHandler <TA> { Run = (trans2, a) => @out.Send(trans2, a) }; var h1 = new TransactionHandler <Event <TA> > { CurrentListener = bea.SampleNoTrans().Listen(@out.Node, trans1, h2, false) }; h1.Run = (trans2, ea) => trans2.Last( new Runnable { Run = () => { if (h1.CurrentListener != null) { h1.CurrentListener.Unlisten(); } h1.CurrentListener = ea.Listen(@out.Node, trans2, h2, true); } }); Listener l1 = bea.Updates().Listen(@out.Node, trans1, h1, false); return(@out.AddCleanup(l1)); }
/// ///Apply a value inside a behavior to a function inside a behavior. This is the ///primitive for all function lifting. /// public static Behavior <TB> Apply <TA, TB>(Behavior <Func <TA, TB> > bf, Behavior <TA> ba) { var @out = new EventSink <TB>(); var h = new Handler <Transaction> { Fired = false }; h.Run = trans1 => { if (h.Fired) { return; } h.Fired = true; trans1.Prioritized( @out.Node, new Handler <Transaction> { Run = trans2 => { @out.Send(trans2, bf.NewValue()(ba.NewValue())); h.Fired = false; } }); }; Listener l1 = bf.Updates().Listen_(@out.Node, new TransactionHandler <Func <TA, TB> > { Run = (trans1, f) => h.Run(trans1) }); Listener l2 = ba.Updates().Listen_(@out.Node, new TransactionHandler <TA> { Run = (trans1, a) => h.Run(trans1) }); return(@out.AddCleanup(l1).AddCleanup(l2).HoldLazy(() => bf.SampleNoTrans()(ba.SampleNoTrans()))); }
/// <summary> /// A stream that gives the updates/steps for a behavior. /// </summary> /// <typeparam name="T">The type of the values in the behavior.</typeparam> /// <param name="b"></param> /// <returns></returns> /// <remarks> /// This is an OPERATIONAL primitive, which is not part of the main Sodium /// API. It breaks the property of non-detectability of behavior steps/updates. /// The rule with this primitive is that you should only use it in functions /// that do not allow the caller to detect the behavior updates. /// </remarks> public static Stream <T> Updates <T>(Behavior <T> b) => Transaction.Apply( trans => b.Updates().Coalesce(trans, (left, right) => right), false);
public void Loop(Behavior <TA> aOut) { ((EventLoop <TA>)Event).Loop(aOut.Updates()); EventValue = aOut.Sample(); }