如何在Javascript中实现Haskell的FRP行为类型?



我想了解Haskell中功能反应性编程的原始含义,以及它与javaScript中FRP的实际应用有何不同。不幸的是,我对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:"));

即使FRP事件和DOM事件没有共同之处,您对behavior的实现也更接近FRP术语中的事件。FRP以此为核心,提取了连续时间(而不是离散时间)的概念。Behavior a表示a类型的值的连续流;因此,它的含义是随时间到值的函数。

字面上是Conal Eliott的定义:

μ :: Behaviour a -> (Time -> a)

符号是他用来描述某物作为从该事物到具体计算值的函数的含义的符号。

事件是"及时冻结的行为",即它们表示在特定时刻发生的值:

μ :: Event a -> [(Time, a)]

因为Haskell是一种懒惰的语言,因此可以将值表表示为列表,这就是上述内容。

奇怪的是,FRP的实现并不多,这与原始想法保持真实,因为(我认为)很难提出连续时间的表现,这似乎很接近。p>我鼓励您观看Conal Eliott在网上提供的演讲,他是我见过的一些最优雅的API设计,他以Crystal Clarity进行了解释。

最新更新