m-signal

0.1.0 • Public • Published

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));

Package Sidebar

Install

npm i m-signal

Weekly Downloads

1

Version

0.1.0

License

MIT

Last publish

Collaborators

  • j201