• Jump To … +
    behavior.js byte-stream.js clock.js iteration.js observable.js operators.js promise-queue.js promise.js stream.js task.js
  • behavior.js

  • ¶
  • ¶

    A behavior is a pull or poll representation of a value that varies continuously over time. Behaviors only model the getter side of the getter, setter, value triad because they produce the value for any given time on demand.

    
    "use strict";
    
    var WeakMap = require("collections/weak-map");
    var Operators = require("./operators");
    var Iteration = require("./iteration");
  • ¶

    BehaviorHandler

  • ¶

    The private handler for a behavior captures its internal state.

    function BehaviorHandler(callback, thisp) {
        this.callback = callback;
        this.thisp = thisp;
    }
  • ¶

    We have to set this up before constructing singleton behaviors.

    var handlers = new WeakMap();
  • ¶

    Behavior

    The behavior constructor accepts a method and optional context object for that method. This method will be polled at each time the behavior is called upon to produce a new value.

    module.exports = Behavior;
    function Behavior(callback, thisp) {
  • ¶

    Behaviors use a weak map of hidden records for private state.

        var handler = new BehaviorHandler(callback, thisp);
        handlers.set(this, handler);
    }
  • ¶

    The yield static method creates a behavior that provides the given constant value.

    Behavior.yield = function (value) {
        return new Behavior(function () {
            return value;
        });
    };
  • ¶

    The index singleton behavior provides the time or “index” any time it is polled.

    Behavior.index = new Behavior(function (index) {
        return index;
    });
  • ¶

    The next method of a behavior returns an iteration that captures both the value and index time, and gives a behavior the same shape as an iterator. Unlike a stream, the next method does not return a promise for an iteration.

    Behavior.prototype.next = function (index) {
        var handler = handlers.get(this);
        var value = handler.callback.call(handler.thisp, index);
        return new Iteration(value, false, index);
    };
  • ¶

    The static lift method accepts an operator and its context object and produces the analogous operator on behaviors. The resulting operator accepts and returns behaviors instead of values. Each time the user polls the returned behavior they will get the result of applying the current value of each behavior argument to the operator.

    For example, Beahavior.lift(add) will produce a behaviorAdd operator. behaviorAdd(Behavior.return(10), Behavior.return(20)) will produce a behavior that will always yield 30.

    Behavior.lift = function (operator, operators) {
        return function operatorBehavior() {
            var operands = Array.prototype.slice.call(arguments); /* TODO unroll */
            return new Behavior(function (index) {
                var values = operands.map(function (operand) {
                    return operand.next(index).value;
                });
                if (values.every(Operators.defined)) {
                    return operator.apply(operators, values);
                }
            });
        };
    };
  • ¶

    The tupleLift static method is the same as lift accept that the returned behavior operator accepts an array of behaviors instead of variadic behavior arguments.

    Behavior.tupleLift = function (operator, operators) {
        return function operatorBehavior(operands) {
            return new Behavior(function (index) {
                var values = operands.map(function (operand) {
                    return operand.next(index).value;
                });
                if (values.every(Operators.defined)) {
                    return operator.apply(operators, values);
                }
            });
        };
    };
  • ¶

    Using lift and tupleLift, we generate both a method for both the behavior constructor and prototype for each of the operators defined in the Operators module.

    for (var name in Operators) {
        (function (operator, name) {
            Behavior[name] = Behavior.lift(operator, Operators);
            var tupleLift = Behavior.tupleLift(operator, Operators);
            Behavior.prototype[name] = function () {
                var operands = [this];
                for (var index = 0; index < arguments.length; index++) {
                    operands[index + 1] = arguments[index];
                }
                return tupleLift(operands);
            };
        })(Operators[name], name);
    }