函数式响应式编程相较于事件监听器的优势

10

我最近听说了很多关于函数式响应式编程的内容,并决定了解一下这是什么。通过阅读bacon.js文档,我发现主要区别在于,与其在组件上设置事件监听器,我会在它上面创建一个事件流,并将事件处理程序传递到该流中。换句话说,我所做的只是将事件处理程序从组件移到了事件流中。就是这样吗?如果是这样,这样做的巨大优势是什么呢?


1
嗯,对我来说,首页上的示例更具声明性和鲁棒性。你不需要编写代码来监听#text中的keydown事件,并注册和取消超时以忽略在300毫秒内输入后跟随的更多输入,并维护最近输入的列表以避免向服务器发送不必要的请求,而只需指定:我想要带有300毫秒暂停的keydown事件,但请勿重复。随着更复杂的逻辑,对比变得更加明显。 - user395760
2个回答

16

函数响应式编程(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值在任何给定时刻的信息。特别地,您可以看到它可以通过buttonUpbuttonDown进行更改,仅此而已。您不必搜索代码以查找可能更改计数器的位置,只需查看其声明并从那里跟进即可。
这就是为什么FRP代码比基于事件的混乱代码少出错的原因。
另请参见我的threepenny-gui库的一些初步文档

1
counter的值是多少?你必须查看代码中所有引用名称为counter的部分,因为那里可能会更改它。”——除非你没有将counter声明为全局变量,而是将其封装在具有明确定义接口的某个对象中。这样,一个简单的counter.getValue()就可以很可靠地达到效果... - rodrigo-silveira
4
我的意思是,要了解 counter.getValue() 返回的值,您需要在代码中查找所有 counter.setValue(...) 的出现。与之相反,使用 FRP,这些出现都可以从 counter 的声明中访问到。 - Heinrich Apfelmus
所以重点是1.) 能够在一个地方表达变化的值,对吧?2.) 为了实现这一点,“一个地方”必须能够访问所有相关信息,该值取决于->因此,您获取信息的位置必须像列表(流)一样,因此它可以携带不同类型的信息片段(事件)。3.) 假设我们有列表(流),我们也可以在更高的级别上操作它们(请参见@Bergi的答案),但这不是主要目的或原始意图,而是这种设置使其成为可能的有用巧合和功能。还是它是一个主要特征? - PEZO

8

就这样吗?

不,它是关于拥有事件流。最终你仍然会将监听器附加到其中以执行效果,但在源和目的地之间,你拥有一个非常强大的抽象。

这样做的最大优势是什么?

事件流确实有很多高阶函数可以轻松处理它们,并且可以通过组合它们而不编写所有容易出错的样板代码。

正如文档中所说的那样,Bacon

将您的事件混乱变成干净和声明性的风水培根,通过从命令式转换为函数式。这就像用函数式编程概念(如map和filter)替换嵌套的for循环。不要再单独处理事件,而是使用事件流进行工作。使用merge和combine结合您的数据,以及使用更重的武器,例如flatMap和combineTemplate。

因此,将事件流进行转换的优点是使代码保持更清晰,而不是嵌套事件处理程序? - tldr
是的。具有清晰代码的所有优点:易懂、易读、无错误、简洁。使用在库中实现的FRP的明确定义的语义,也可以对代码进行推理和证明正确性。 - Bergi
2
而且,函数式编程的关键不仅在于转换(例如简单的filter/map/reduce操作),还在于可组合性 - Bergi
是的,这正是相同的意大利面问题。Promises和Properties之间只有一个小的语义差异:Promises将单个异步成功/失败值包装到它们解决的值中,而Properties随时间更改,即多次获得新值。您经常会一起使用它们。我无法告诉您与Angular相比的确切优势,因为我还没有真正理解他们的哲学,但从我的角度来看,Angular看起来过于命令式,自动传播赋值(至少大部分时间如此,似乎存在一些不一致性)。 - Bergi
1
@PEZO 在引用中提到了:干净和声明式的函数式编程。当然,我可能没有像海因里希·阿普菲尔穆斯那样表达得那么优美,但它确实意味着相同的事情。我没有抄袭他的话,只是点赞了他的回答 :-) 不,没有任何权威机构能够给出一个关于FRP术语的唯一真正定义。 - Bergi
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接