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

时间:2017-02-21 17:21:06

标签: javascript haskell functional-programming reactive-programming 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远程可比吗?

这是一个小例子 - 鼠标点击计数器(计数4秒):

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:"));

1 个答案:

答案 0 :(得分:1)

behavior的实现更接近FRP术语中的事件,即使FRP事件和DOM事件没有任何共同之处。 FRP的核心是抽象连续(相对于离散)时间的概念。 Behavior a代表a类型的连续价值流;因此,意义是一个时间到价值的函数。

字面意思是Conal Eliott如何定义它:

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

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

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

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

因为Haskell是一种惰性语言,所以值流可以表示为列表,这就是上面所说的。

奇怪的是,没有太多FRP的实现仍然适用于最初的想法,因为(我认为)很难提出连续时间的高效实现 - this C++ one seems to come close

我鼓励您观看Conal Eliott在线提供的演讲,他是我见过的最优雅的API设计,他以清晰的方式解释了它。