对不起,我刚刚开始研究反应性香蕉和FRP。
反应香蕉的作者根据我的建议制作了this例子,其中他创造了一个可以增加和减少的计数器。他使用累积函数来累积事件。我想我能够在某种程度上了解事件类型,并能够用它来测试很多东西,但后来我记得还有行为。我调查了一下,但似乎这种行为意味着在类似的情况下使用;修改现有变量,就像accumE对事件一样。
行为意味着什么,它的用例是什么?
答案 0 :(得分:11)
我同意Ankur而不是Chris:文本框是一个随时间变化的值,因此自然希望成为行为而不是事件。 Chris给出不太自然的事件选择的原因是实现问题,因此(如果准确的话)是反应性香蕉实现的一个不幸的工件。我非常宁愿看到实现得到改进而不是非自然地使用的范例。
除了语义匹配之外,选择Behavior
而不是Event
在实用上非常有用。例如,您可以使用Applicative
操作(例如liftA2
)将时变文本框值与其他时变值(行为)相结合。
答案 1 :(得分:7)
从语义上讲,你有
Behavior a = Time -> a
也就是说,Behavior a
是a
类型的值,随时间变化。一般来说,当 Behavior a
发生变化时,你对一无所知,所以对于点击按钮更新文本字段来说,这是一个相当糟糕的选择。也就是说,获取表示计数器示例中数字当前值的行为很容易。只需在事件流上使用stepper
,或者以相同的方式从头开始构建它,除非使用accumB
而不是accumE
。
通常,您连接到输入和输出的内容始终为Event
s,因此Behavior
在内部用于中间结果。
假设在给定的示例中,您想要添加一个记住当前值的新按钮,就像简单计算器上的记忆功能一样。您可以通过为记住的值添加内存按钮和文本字段来开始:
bmem <- button f [text := "Remember"]
memory <- staticText f []
您需要能够随时询问当前值,因此在您的网络中,您将添加一种行为来表示它。
let currentVal = stepper 0 counter
然后你可以挂钩事件,并在每次按下Remember按钮时使用apply
读取行为的值,并产生一个具有该值序列的事件。
emem <- event0 bmem command
let memoryE = apply (const <$> currentVal) emem
最后,将这个新事件连接到输出
sink memory [text :== ("", show <$> memoryE)]
如果你想在内部使用内存,那么你也需要一个Behavior
作为其当前值...但由于我们只使用它来将它连接到输出,我们只需要一个事件现在。
这有帮助吗?
答案 2 :(得分:6)
图书馆作者发言。 : - )
显然,Chris Smith可以读懂思想,因为他准确地描述了我的想法。 : - )
但Conal和Arthur也有一点意义。从概念上讲,计数器是一个随时间变化的值,而不是一系列事件发生。因此,将其视为Behavior
会更合适。
不幸的是,行为没有任何关于何时会改变的信息,而是“仅民意调查”。现在,我可以尝试实现各种巧妙的方案,这将最小化轮询,从而允许GUI元素的有效更新。 (Conal在original paper中做了类似的事情。)但我采用了“没有魔法”的理念:图书馆用户应负责通过事件自己管理更新。
我目前设想的解决方案是提供除Event
和Behavior
之外的第三类型,即Reactive
(名称可能会更改),其体现了两者:从概念上讲,它是一个随时间变化的值,但它也带有一个通知变化的事件。一种可能的实现方式是
type Reactive a = (a,Event a)
changes :: Reactive a -> Event a
changes (_, e) = e
value :: Reactive a -> Behavior a
value (x, e) = stepper x e
毫无疑问,这正是sink
期望的类型。这将包含在反应香蕉库的未来版本中。
编辑:我发布了反应性香蕉version 0.4,其中包含新类型,现在称为Discrete
。
答案 3 :(得分:5)
通常,行为是在一段时间内发生变化的值。它是一个连续值,其中事件是离散值。在行为的情况下,始终存在值。 例如:文本框上的文本是行为,因为文本可以在一段时间内更改,但会有一个当前值,其中作为事件中的键盘笔划,因为您无法查询键盘笔划的“当前”值。