Many believe that FRP involves managing event streams without the need to explicitly handle state. An example of this can be found here:
Others argue in favor of FRP by highlighting the challenges associated with programming solely through side-effects, as is often required with asynchronous callbacks.
Despite experimenting with FRP (specifically flapjax), a recurring issue arises: difficulty in managing state except through explicit side-effects.
For instance, consider an animation queue where changes are received on an event stream. Upon receiving the first change, there is a need to schedule a draw operation for future execution (e.g., using window.requestAnimationFrame) and accumulate changes until the scheduled draw occurs. When the draw event takes place, the accumulated changes must then be rendered.
In an imperative style using the observer pattern, this task can be achieved in about six lines of code. However, translating this into FRP proves challenging. The closest solution often involves encapsulating related event streams around a shared state, ultimately necessitating explicit state management and rendering events through side-effects. This approach doesn't seem like a significant improvement over traditional imperative callbacks.
The question remains: how should such scenarios be addressed within the realm of FRP?
Here's a utility function from flapjax that showcases closure over state:
function worldE(init, handlers) {
var r = fj.receiverE();
fj.forEach(function(h) {
h[0].mapE(function (ev) {
r.sendEvent(init = h[1](init, ev));
});
}, handlers);
return r;
}
Now, let's see how it can be utilized within an animation loop:
function initialize(opts) {
var blitE = fj.receiverE();
function accumulate(state, data) {
if (!state.queued) {
window.requestAnimationFrame(blitE.sendEvent);
}
return {queued: true, changes: _.extend({}, state.changes, data)};
}
function dodraw(state, _) {
draw(state.changes);
return {queued: false, changes: {}};
}
worldE({queued: false, changes: {}},
[[opts.data_source, accumulate], [blitE, dodraw]]);
}
It's evident that this implementation is bulkier, less readable, and harder to maintain compared to equivalent callback-based code. It still heavily relies on explicit state management and operates through side effects.
This begs the question: is there a more efficient way to tackle such scenarios in FRP? Perhaps utilizing a different pattern or exploring alternative libraries could offer a better solution.