m-signal v0.1.0
#m-signal
Monadic signals for asynchronous and reactive programming. Still under development, but should be usable.
##API
signal(source)
Creates a signal. source should be a function that takes one argument: a function that source calls when it wants to add a value to the signal.
signal.unit(value)
Creates a signal with value as its only value. (Part of being a monad, and equivalent to monadic return.)
signal.combine(...signals)
Takes a number of signals and combines them into one signal that broadcasts the current value of each signal as an array when any signal changes.
signal.lift(f)
Takes a function and returns an equivalent where each argument and the return value are all signals containing the same type as the original function.
###Methods of signals
In the following descriptions, s is any signal. All methods return a new signal except for listen.
s.fmap(f)
Returns a signal with every value broadcast by s passed through the function f. Analogous to map for arrays.
s.bind(f)
f should be a function that takes a value broadcast by s and returns a signal. The signal returned by bind then broadcasts the values of these signals whenever one of them updates.
s.fold(f, i)
For each value from f, passes the previous return value of f and the new value to f and broadcasts the return value. i is used as the initial first argument to f.
s.apply(s2)
This only works if the values of s are functions. When s or s2 is updated, broadcasts the result of calling the current value of s with the current value of s2.
s.listen(f)
Whenever s broadcasts a value, f is called with that value. Note that the value can be the same as the previous one and that if s has already broadcast a value when listen is called, f will be immediately called with the current value of s.
###Types
(In my own imagined TypeScript/Haskell hybrid type system, hopefully it's comprehensible.)
type Signal<T> = {
	fmap: <U> (T => U) => Signal<U>,
	bind: <U> (T => Signal<U>) => Signal<U>,
	fold: <U> ((U, T) => U, U) => Signal<U>,
	apply: <U => V = T> Signal<U> => Signal<V>,
	listen: (T => void) => void
};
signal :: <T> ((T => void) => void) => Signal<T>;
signal.unit :: <U> U => Signal<U>
signal.combine :: <T, U,...> [Signal<T>, Signal<U>,...] => Signal<[T, U,...]>
signal.lift :: <T, U,...,R> ((T, U,...) => R) => ((Signal<T>, Signal<U>,...) => Signal<R>)##Examples
###FRP
var signal = require('msignal');
function mouseSignal() {
	return signal(function(resolve) {
		window.addEventListener("mouseup", function(e) { resolve(false); });
		window.addEventListener("mousedown", function(e) { resolve(true); });
	});
}
// Logs the number of clicks
mouseSignal()
	.fold(function(sum, mouse) { return mouse ? sum + 1 : sum; }, 0)
	.listen(console.log.bind(console));###Promise-y
var signal = require('msignal');
function ajaxGet(url) {
	return signal(function(resolve) {
		var xhr = new XMLHttpRequest();
		xhr.open("GET", url);
		xhr.onreadystatechange = function() {
			if (xhr.readyState === 4 && xhr.status === 200)
				resolve(xhr.responseText);
		};
		xhr.send();
	});
}
ajaxGet("http://reddit.com/r/javascript/about.json")
	.fmap(function(json) {
		return JSON.parse(json).data.description_html;
	})
	.listen(console.log.bind(console));