-
Notifications
You must be signed in to change notification settings - Fork 109
/
behavior.js
117 lines (101 loc) · 4.12 KB
/
behavior.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
// 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);
}