Graphical User Interface
Internal State
{ paused: false, time: 0 }
var time = 0;
var paused = false;
setInterval(function() {
if (!paused) {
time++;
$("#time").text("Time: " + time);
}
}, 1000);
$("#button").click(function() {
paused = !paused;
$("#time").text(
paused ? "Paused" : "Time: " + count);
});
var pausedSig = Rx.Observable
.fromEvent($("#button"), "click")
.scan(function(paused) {return !paused;},false)
.startWith(false);
var timeSig = Rx.Observable.interval(1000)
.pausable(pausedSig)
.scan(function(c) { return c + 1; }, 0)
.startWith(0);
Rx.Observable.combineLatest(timeSig, pausedSig,
function(time, paused) {
return paused ? "Paused" : "Time: " + time;
})
.subscribe(function(s){$("#time").text(s);});
Advantages
Disadvantages
23
, "foo"
, [1,2,3]
Goal: Reactive Programming without Signals
let i = 0;
let j = i + 1;
i = 3;
j = i + 1;
console.log(j); // 14
rlet i = 0;
rlet j = i + 1;
i = 3;
console.log(j); // 4
Normal assignments do not update dependent variables
Assignment to reactive variable i
also updates dependent variable j
rlet paused = initially(false)
subscribe($("#button").click)
!paused;
rlet time = initially(0)
subscribe(interval(1000))
paused ? time : time + 1;
rlet txt = paused ? "Paused" : "Time: " + time;
subscribe(txt) { $("#time").text(txt); }
Assigning new values with imperative push updates
rlet txt;
txt = "foo";
Alternatively: Using subscribe
syntactic sugar to automatically trigger updates for JavaScript functions expecting a callback
rlet txt = subscribe(input.changed) input.text();
Reactive variable referenced by other reactive variables automatically set up a dependency
rlet len = txt.length;
Alternatively, subscribe
can be used to denote a dependency without using the value
rlet lastChanged = subscribe(txt) Date.now();
A reactive variable reference in its own definition denotes the previous value to enable stateful computation
rlet numChanged = subscribe(txt)
initially(0) numChanged + 1;
Assignments might update dependent reactive variables which can be accessed like normal variables
console.log(numChanged); // "23"
txt = "bar";
console.log(numChanged); // "24"
Alternatively, a special subscribe
syntax can be used to invoke imperative code at each change
subscribe(numChanged) { console.log("goo"); }
txt = "bar"; // "goo"
rlet
is a macro-generating macro (using the sweet.js macro system)
Github Repository: https://github.com/levjj/rde/
Online Live Demo: https://levjj.github.io/rde/
rlet i = 0;
rlet j = i + 1;
i = 3;
console.log(j);
var S_i = new Sig(function(){
return 0;
}, null);
var S_j = new Sig(function(){
return IMM(S_i).read() + 1;
}, null);
S_i.onUpdate(S_j);
S_i.push(3);
console.log(S_j.read());
Manipulating global state during change propagation results in additional, non-reactive data dependencies
var x = 1;
Rx.Observable.interval(100)
.map(function() { return x * x; }) // x2 ≈ x*x
.map(function(x2) { x++; return x2; });
.subscribe(function(x2) { alert([x, x2]); });
// ≈ alert([x, x*x]);
[1,1], [2,4], [3,9], [4,16] ...
[2,1], [3,4], [4,9], [5,16] ...
All references to surrounding variables in update expression of rlet
are wrapped in IMM()
function IMM(x) {
return new Proxy(x, {
get: function(target, key) {
return IMM(target[key]); },
set: function() {
throw new TypeError('immutable!'); }
});
};
Advantages
Disadvantages
Static mapping of reactive dependencies to lexical scopes as basis for debugging and visualizations for reactive programming
Asynchronous updates of reactive variables
Continuous reactive variables that do not propagate updates with unchanged values