1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
//! *Carboxyl* provides primitives for functional reactive programming in Rust.
//! It draws inspiration from the [Sodium][sodium] libraries and Push-Pull FRP,
//! as described by [Elliott (2009)][elliott_push_pull].
//!
//! [sodium]: https://github.com/SodiumFRP/sodium/
//! [elliott_push_pull]: http://conal.net/papers/push-pull-frp/push-pull-frp.pdf
//!
//!
//! # Overview
//!
//! Functional reactive programming (FRP) is a composable and modular
//! abstraction for creating dynamic and reactive systems. In its most general
//! form it models these systems as a composition of two basic primitives:
//! *streams* are a series of singular events and *signals* are continuously
//! changing values.
//!
//! *Carboxyl* is an imperative, hybrid push- and pull-based implementation of
//! FRP. Streams and the discrete components of signals are data-driven, i.e.
//! whenever an event occurs the resulting changes are propagated to everything
//! that depends on it.
//!
//! However, the continuous components of signals are demand-driven. Internally,
//! *Carboxyl* stores the state of a signal as a function. This function has to
//! be evaluated by consumers of a signal to obtain a concrete value.
//!
//! Nonetheless, *Carboxyl* has no explicit notion of time. Its signals are
//! functions that can be evaluated at any time, but they do not carry any
//! inherent notion of time. Synchronization and atomicity is achieved by a
//! transaction system.
//!
//!
//! # Functional reactive primitives
//!
//! This library provides two basic types: `Stream` and `Signal`. A stream is a
//! discrete sequence of events, a signal is a container for values that change
//! (discretely) over time.
//!
//! The FRP primitives are mostly implemented as methods of the basic types to
//! ease method chaining, except for the various lifting functions, as they do
//! not really belong to any type in particular.
//!
//! In addition, the `Sink` type allows one to create a stream of events by
//! sending values into it. It is the only way to create a stream from scratch,
//! i.e. without using any of the other primitives.
//!
//!
//! # Usage example
//!
//! Here is a simple example of how you can use the primitives provided by
//! *Carboxyl*. First of all, events can be sent into a *sink*. From a sink one
//! can create a *stream* of events. Streams can also be filtered, mapped and
//! merged. One can e.g. hold the last event from a stream as a signal.
//!
//! ```
//! use carboxyl::Sink;
//!
//! let sink = Sink::new();
//! let stream = sink.stream();
//! let signal = stream.hold(3);
//!
//! // The current value of the signal is initially 3
//! assert_eq!(signal.sample(), 3);
//!
//! // When we fire an event, the signal get updated accordingly
//! sink.send(5);
//! assert_eq!(signal.sample(), 5);
//! ```
//!
//! One can also directly iterate over the stream instead of holding it in a
//! signal:
//!
//! ```
//! # use carboxyl::Sink;
//! # let sink = Sink::new();
//! # let stream = sink.stream();
//! let mut events = stream.events();
//! sink.send(4);
//! assert_eq!(events.next(), Some(4));
//! ```
//!
//! Streams and signals can be combined using various primitives. We can map a
//! stream to another stream using a function:
//!
//! ```
//! # use carboxyl::Sink;
//! # let sink: Sink<i32> = Sink::new();
//! # let stream = sink.stream();
//! let squares = stream.map(|x| x * x).hold(0);
//! sink.send(4);
//! assert_eq!(squares.sample(), 16);
//! ```
//!
//! Or we can filter a stream to create a new one that only contains events that
//! satisfy a certain predicate:
//!
//! ```
//! # use carboxyl::Sink;
//! # let sink: Sink<i32> = Sink::new();
//! # let stream = sink.stream();
//! let negatives = stream.filter(|&x| x < 0).hold(0);
//!
//! // This won't arrive at the signal.
//! sink.send(4);
//! assert_eq!(negatives.sample(), 0);
//!
//! // But this will!
//! sink.send(-3);
//! assert_eq!(negatives.sample(), -3);
//! ```
//!
//! There are some other methods on streams and signals, that you can find in
//! their respective APIs.
//!
//! Note that all these objects are `Send + Sync + Clone`. This means you can
//! easily pass them around in your code, make clones, give them to another
//! thread, and they will still be updated correctly.
//!
//! You may have noticed that certain primitives take a function as an argument.
//! There is a limitation on what kind of functions can and should be used here.
//! In general, as FRP provides an abstraction around mutable state, they should
//! be pure functions (i.e. free of side effects).
//!
//! For the most part this is guaranteed by Rust's type system. A static
//! function with a matching signature always works. A closure though is very
//! restricted: it must not borrow its environment, as it is impossible to
//! satisfy the lifetime requirements for that. So you can only move stuff into
//! it from the environment. However, the moved contents of the closure may also
//! not be altered, which is guaranteed by the `Fn(…) -> …)` trait bound.
//!
//! However, both closures and functions could still have side effects such as
//! I/O, changing mutable state via `Mutex` or `RefCell`, etc. While Rust's type
//! system cannot prevent this, you should generally not pass such functions to
//! the FRP primitives, as they break the benefits you get from using FRP.
//! (An exception here is debugging output.)

#![warn(missing_docs)]

#[cfg(test)]
extern crate rand;
#[cfg(test)]
extern crate quickcheck;
#[macro_use(lazy_static)]
extern crate lazy_static;

pub use stream::{ Sink, Stream };
pub use signal::{ Signal };

mod transaction;
mod source;
mod pending;
mod stream;
mod signal;
mod fnbox;
#[macro_use]
pub mod lift;
#[cfg(test)]
mod testing;