"use strict";
var WeakMap = require("collections/weak-map");
var Operators = require("./operators");
var Iteration = require("./iteration");
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");
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();
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);
}