我想了解 Haskell 中函数式响应式(Reactive)编程的本意,以及它与 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
仅将给定的处理程序订阅到给定的 DOM HTML 元素。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:"));
最佳答案
您对 behavior
的实现更接近 FRP 术语中的事件,即使 FRP 事件和 DOM 事件没有任何共同之处。 FRP 的核心是抽象连续(相对于离散)时间的概念。 Behavior a
表示 a
类型的连续值流;因此,它的含义是从时间到值(value)的函数。
Conal Eliott 是这样定义的:
μ :: Behaviour a -> (Time -> a)
该符号是他用来将某事物的含义描述为从该事物到具体计算值的函数的符号。
事件是“及时卡住的行为”,即它们代表在特定时刻发生的值:
μ :: Event a -> [(Time, a)]
因为 Haskell 是一种惰性语言,值流可以表示为列表,这就是上面所说的。
奇怪的是,并没有多少 FRP 的实现仍然忠实于最初的想法,因为(我认为)很难提出连续时间的高性能实现——this C++ one seems to come close .
我鼓励你在线观看 Conal Eliott 的演讲,他是我见过的一些最优雅的 API 设计,他解释得非常清楚。
关于javascript - 如何在 Javascript 中实现 Haskell 的 FRP Behavior 类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42373923/