Sinkline is a C++14 library for structuring callbacks in a declarative, composable way, inspired by functional reactive programming (FRP), Reactive Extensions (Rx), and ReactiveCocoa (RAC).
Sinkline’s fundamental unit of abstraction is the sink. Sinks are used just like normal callbacks (e.g., lambdas or functions), but can be inspected and composed in interesting ways, using functional primitives like map
and filter
.
Sinkline is designed with the following goals in mind, in no particular order.
Although it is sometimes impossible to satisfy all of these goals at once, the principles underlying the goals are the most important, and all changes to the library should be made with them in mind.
It should be easy to “get your feet wet” with Sinkline, and start using it immediately without having to read a bunch of accompanying documentation or literature.
This means that the library should be accessible and offer incremental benefits. It should be possible to start using it within an existing codebase—even within just one function—and see some benefit.
One of the problems with Observable
s in Rx (and SignalProducer
s in ReactiveCocoa) is that they make it very difficult to reason about effects. Does subscribing to an Observable
start work each time, just once, or never? Are values buffered, multicasted, or neither?
Unfortunately, it is very difficult for any library to definitively answer those questions. Instead, Sinkline should be a transparent abstraction that doesn't make it harder to reason about the underlying code.
Existing code should not have to be refactored to “speak in the language” that Sinkline uses.
Instead, the library should offer transparent interoperability with other methods of asynchronous programming. For example, it should be possible to use sinks instead of callbacks without needing to refactor the callback-based APIs.
Concurrency and state are extremely difficult to manage correctly, so the library should take responsibility for both whenever possible.
Practically, this might mean handling synchronization automatically "behind the scenes," or offering ways to replace stateful variables with functional primitives (e.g. scan
instead of a stateful accumulator).
The sink abstractions should not impose a performance penalty.
Although true “zero overhead” abstraction is more-or-less impossible, the library should do as much as possible to eliminate overhead. Ideally, sink-based code should match the performance of imperative equivalents (though not necessarily imperative code that has been extensively optimized by hand).
Sinkline uses Buck to build. Once you have installed Buck, you can build the library like so:
buck build //sinkline
To run the tests:
buck test //sinkline
There is also an example iOS application which uses Sinkline. To build and run it in the iOS simulator:
buck install -r //example:GIFList#iphonesimulator-x86_64
The above instructions assume the Clang compiler. To use GCC instead, you must specify a configuration file:
buck build --config //buildfile.includes=//script/gcc_defs.py
This same --config
flag can also be passed to buck test
.
Sinkline is MIT licensed.