Struct carboxyl::Signal
[−]
[src]
pub struct Signal<A> { // some fields omitted }
A continuous signal that changes over time.
Signals can be thought of as values that change over time. They have both a continuous and a discrete component. This means that their current value is defined by a function that can be called at any time. That function is only evaluated on-demand, when the signal's current value is sampled. (This is also called pull semantics in the literature on FRP.)
In addition, the current function used to sample a signal may change discretely in reaction to some event. For instance, it is possible to create a signal from an event stream, by holding the last event occurence as the current value of the stream.
Algebraic laws
Signals come with some primitive methods to compose them with each other and with streams. Some of these primitives give the signals an algebraic structure.
Functor
Signals form a functor under unary lifting. Thus, the following laws hold:
- Preservation of identity:
lift!(|x| x, &a) == a
, - Function composition:
lift!(|x| g(f(x)), &a) == lift!(g, &lift!(f, &a))
.
Applicative functor
By extension, using the notion of a signal of a function, signals also
become an applicative using Signal::new
as pure
and
|sf, sa| lift!(|f, a| f(a), &sf, &sa)
as <*>
.
TODO: Expand on this and replace the Haskell reference.
Methods
impl<A: Clone + 'static> Signal<A>
fn new(a: A) -> Signal<A>
Create a constant signal.
fn sample(&self) -> A
Sample the current value of the signal.
impl<A: Clone + Send + Sync + 'static> Signal<A>
fn cyclic<F>(def: F) -> Signal<A> where F: FnOnce(&Signal<A>) -> Signal<A>
Create a signal with a cyclic definition.
The closure gets an undefined forward-declaration of a signal. It is supposed to return a self-referential definition of the same signal.
Sampling the forward-declared signal, before it is properly defined, will cause a run-time panic.
This pattern is useful to implement accumulators, counters and other loops that depend on the sampling behaviour of a signal before a transaction.
fn snapshot<B, C, F>(&self, stream: &Stream<B>, f: F) -> Stream<C> where B: Clone + Send + Sync + 'static, C: Clone + Send + Sync + 'static, F: Fn(A, B) -> C + Send + Sync + 'static
Combine the signal with a stream in a snapshot.
snapshot
creates a new stream given a signal and a stream. Whenever
the input stream fires an event, the output stream fires an event
created from the signal's current value and that event using the
supplied function.
let sink1: Sink<i32> = Sink::new(); let sink2: Sink<f64> = Sink::new(); let mut events = sink1.stream().hold(1) .snapshot(&sink2.stream(), |a, b| (a, b)) .events(); // Updating its signal does not cause the snapshot to fire sink1.send(4); // However sending an event down the stream does sink2.send(3.0); assert_eq!(events.next(), Some((4, 3.0)));
impl<A: Clone + Send + Sync + 'static> Signal<Signal<A>>
fn switch(&self) -> Signal<A>
Switch between signals.
This transforms a Signal<Signal<A>>
into a Signal<A>
. The nested
signal can be thought of as a representation of a switch between different
input signals, that allows one to change the structure of the dependency
graph at run-time. switch
provides a way to access the inner value of
the currently active signal.
The following example demonstrates how to use this to switch between two
input signals based on a Button
event stream:
// Button type #[derive(Clone, Show)] enum Button { A, B }; // The input sinks let sink_a = Sink::<i32>::new(); let sink_b = Sink::<i32>::new(); // The button sink let sink_button = Sink::<Button>::new(); // Create the output let output = { // Hold input sinks in a signal with some initials let channel_a = sink_a.stream().hold(1); let channel_b = sink_b.stream().hold(2); // A trivial default channel used before any button event let default_channel = Sink::new().stream().hold(0); // Map button to the channel signals, hold with the default channel as // initial value and switch between the signals sink_button .stream() .map(move |b| match b { Button::A => channel_a.clone(), Button::B => channel_b.clone(), }) .hold(default_channel) .switch() }; // In the beginning, output will come from the default channel assert_eq!(output.sample(), 0); // Let's switch to channel A sink_button.send(Button::A); assert_eq!(output.sample(), 1); // And to channel B sink_button.send(Button::B); assert_eq!(output.sample(), 2); // The channels can change, too, of course for k in 4..13 { sink_b.send(k); assert_eq!(output.sample(), k); } sink_button.send(Button::A); for k in 21..77 { sink_a.send(k); assert_eq!(output.sample(), k); }