I'd like to understand the original meaning of functional reactive programming in Haskell and how it differs from the actual application of FRP in Javascript. Unfortunately, I have only a superficial understanding of Haskell and have to stick with Javascript.
Here is my attempt to implement Haskell's Behavior
data type in an untyped language:
// behavior :: String -> DOMHTMLElement -> a -> (a -> Event -> a) -> (a -> b)
const behavior = type => target => x => f => {
const destructiveSet = y => (x = f(x) (y), x), // A
handler = event => destructiveSet(event);
target.addEventListener(type, handler, true);
return g => g(x);
};
Line A
is necessary, because I have to mutate the initial value x
, which is held by the call stack. The function body evaluates from left to right and returns the value of the last operand of the comma operator ,
, i.e. the destructively updated version of x
.
target.addEventListener
merely subscribes the given handler to the given DOM HTML-element.
behavior
returns a function that enables read-only access to x
.
This implementation introduces a read-only abstract data type in Javascript, whose values solely exist in the call stacks of higher order functions. Provided that DOM Events are only triggered by GUI users, a program has no means to mutate values of type behavior
. It can only poll them to observe the time-varying effect.
Is this approach remotely comparable to Haskell's Behavior
?
Here is a small example - a mouse click counter (counts for four seconds):
const behavior = type => target => x => f => {
const destructiveSet = y => (x = f(x) (y), x),
handler = event => destructiveSet(event);
target.addEventListener(type, handler, true);
return g => g(x);
};
const comp = f => g => x => f(g(x));
const K = x => y => x;
const I = x => x;
const get = I;
const defer = n => f => x => setTimeout(f, n, x);
const log = prefix => x => console.log(prefix, x);
const inc = x => x + 1;
const counter = behavior("click") (document) (0) (comp(K) (inc));
console.log("click on the section above");
counter(get); // 0
defer(4000) (counter) (log("counted clicks:"));