我听说过很多关于函数式响应式编程的事情,所以我决定看看它到底有什么了不起。通过bacon.js文档,似乎主要的区别在于,不是在组件上设置事件侦听器,而是在组件上创建事件流,并将事件处理程序传递到流中。换句话说,我所做的只是将事件处理程序从组件移动到事件流。就是这样吗?如果是这样,那么这样做的最大优势是什么?
函数式响应式编程(FRP)的关键点是一个语法属性:
值的动态行为在声明时间指定。
例如,考虑一个可以通过按按钮向上或向下计数的计数器。在命令式风格中,您可能会这样写:
counter := 0 -- initial value
on buttonUp = (counter := counter + 1) -- change it later
on buttonDown = (counter := counter - 1)
首先,用初始值声明计数器。然后,在代码的后面部分,更改该值。现在,如果有人问你"在任何给定时刻,counter
的值是多少?",你必须查看代码中引用counter
的所有部分,因为这是它可能被改变的地方。
相反,当使用FRP样式代码时,可以通过查看代码中的一个位置来回答这个问题:声明counter
的地方。例如,在Haskell中,可以将计数器写成
counter :: Behavior Int
counter = accumulate ($) 0
(fmap (+1) buttonUp
`union` fmap (subtract 1) buttonDown)
右边包含了在任何给定时刻counter
的值所需的所有信息。特别是,您可以看到它可以通过buttonUp
和buttonDown
, 进行更改,这就是。你不需要筛选你的代码,寻找计数器可能改变的地方,不,只要看看它的声明并从那里开始跟进就足够了。
这就是为什么FRP代码比基于事件的意大利面条代码更不容易出现bug的原因。
参见我的threenpenny -gui库的一些初步文档
是这样吗?
。它是关于拥有事件流。最后,您仍然需要将侦听器附加到它们上以执行效果,但是在源和目标之间,您有一个非常强大的抽象。
这样做的最大优势是什么?
事件流确实有很多高阶函数可以很容易地处理它们,对于来说,组合事件流而不需要写出所有容易出错的样本代码。
就像文档说的那样,bacon
把你的事件面条变成干净的和声明性的风水培根,从命令式切换到功能性。这就像用函数式编程概念(如
map
和filter
)替换嵌套的for
循环。停止处理单个事件,转而处理事件流。将你的数据与merge
和combine
相结合,并使用flatMap
和combineTemplate
等更重的武器。