Я хотел бы понять исходное значение функционального реактивного программирования в Haskell и то, как он отличается от фактического применения FRP в Javascript. К сожалению, у меня есть только поверхностное понимание Haskell и нужно придерживаться Javascript.
Вот моя попытка реализовать тип данных Haskell Behavior
в нетипизированном языке:
// 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);
};
Строка A
необходима, потому что мне нужно изменить начальное значение x
, которое удерживается стеком вызовов. Тело функции оценивается слева направо и возвращает значение последнего операнда оператора запятой ,
, то есть деструктивно обновляемую версию x
.
target.addEventListener
просто подписывает данный обработчик на данный HTML-элемент DOM.
Behavior
возвращает функцию, которая позволяет доступ только для чтения к x
.
В этой реализации вводится абстрактный тип данных для чтения в Javascript, значения которого существуют только в столах вызовов функций более высокого порядка. При условии, что события DOM запускаются только пользователями GUI, программа не имеет средств для изменения значений типа Behavior
. Он может только опросить их, чтобы наблюдать изменяющийся во времени эффект.
Этот подход удаленно сопоставим с Haskell Behavior
?
Вот небольшой пример - счетчик щелчков мыши (рассчитан на четыре секунды):
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:"));