2011-06-30 40 views
12

对不起,我刚开始考察反应香蕉和玻璃钢。反应香蕉行为

反应香蕉的作者根据我的建议做了this的例子,在这个例子中他创建了一个可以增加和减少的计数器。他使用累积事件的accumE函数。我认为我能够对Event类型有所帮助,并且能够用它测试很多事情,但后来我记得还有Behavior。我研究过它,但似乎这种行为意味着在类似的情况下使用;修改现有的变量,就像accumE用于事件一样。

行为是什么意思,它有什么用途?

+0

https://github.com/HeinrichApfelmus/Haskell-BlackBoard/blob/master/reactive-banana-wx/src/Counter.hs is 404 –

回答

7

语义,你有

Behavior a = Time -> a 

也就是说,Behavior aa型随时间变化的值。一般来说,你根本不知道 a Behavior a会改变,所以它变成一个相当糟糕的选择来更新文本字段的点击按钮。也就是说,很容易得到这一行为来表示计数器例子中数字的当前值。只需在事件流上使用stepper,或者使用accumB而不是accumE以相同方式从头开始构建。

通常,您连接到输入和输出的东西始终为Event s,所以Behavior在内部用于中间结果。

假设在给定的例子中,你想添加一个新的记忆当前值的按钮,比如简单计算器上的记忆功能。你会开始时通过添加内存按钮和文本字段的值记住:

bmem <- button f [text := "Remember"] 
memory <- staticText f [] 

你需要能够在任何时间,要求的电流值,使您的网络中,你会添加行为来表示它。

let currentVal = stepper 0 counter 

然后你可以挂钩的事件,并使用apply读取行为的每个记住按下按钮时的价值,并产生与价值观的该序列的事件。

emem <- event0 bmem command 
let memoryE = apply (const <$> currentVal) emem 

最后,挂钩这一新的事件来输出

sink memory [text :== ("", show <$> memoryE)] 

如果你想在内部使用的内存,然后再你想要为它的当前值Behavior太...但因为我们只用它将它连接到输出,所以我们现在只需要一个事件。

这有帮助吗?

+0

是的,我相信这正是我想要的 – Masse

+0

虽然也许是误导,正如我的答案中所解释的。 – Conal

6

通常,行为是一段时间内会发生变化的值。这是一个连续的值,其中事件是离散值。在行为的情况下,值总是存在。 例如:文本框上的文本是行为,因为文本可以在一段时间内更改,但会有当前值,其中作为事件中的键盘笔划,因为您无法为其“当前值”查询键盘笔划“价值。

+1

正如我在我的回答中所提到的,行为实际上并不是很好的匹配用于设置类似于反应式香蕉的文本字段,因为调用底层图形库函数来设置需要在离散时间发生的值,所以实际上需要事件来通知您何时发生更改。 –

+0

我和Ankur在这里,正如我的回答中所反映的。 – Conal

11

我同意Ankur而不是Chris:文本框是一段时间的价值,所以自然而然地想成为一种行为而不是事件。 Chris给出的不太自然的事件选择的原因是实施问题等(如果准确的话)反应性香蕉实施的不幸的人造物。我宁愿看到改进的实现比不自然地使用范例。

除了语义上的适合之外,选择Behavior而非Event的实用性非常有用。例如,您可以使用Applicative操作(例如,liftA2)将时变文本框值与其他时变值(行为)组合。

+0

请注意,文本框的价值本质上是一个*不连续*时间函数,我怀疑这是实现问题和“行为”不太自然的看法的原因。这与运动图像在屏幕上的位置不同,这自然是连续的。文本框的*微分*是“行为”还是“事件”? –

+0

你真的是指dis /连续时间函数,还是dis/continuous time函数?行为是关于连续时间的函数,而不是连续的时间函数。 – Conal

+0

因为它需要离散值,谈论占用时间跨度值的变化是没有意义的。如果随时间的文本框值是* tb(t)*,并且从* tb(n)=“ab”*到* tb(n + 0.5)=“abc”*,则变化是(++“c “)。如果* tb(n + 0.25)= X *,从“ab”到X加上从X到“abc”的变化应该等于(++“c”)。我认为没有语义上有意义的方法可以将(++“c”)分成两半,所以变化必须发生在X之前或之后。重复参数显示变化必须具有持续时间0,使* tb(t)*一个不连续的功能。 –

6

图书馆作者讲。 :-)

显然,Chris Smith可以阅读头脑,因为他准确地描述了我的想法。 :-)

但是ConalArthur也有一点。从概念上讲,计数器是一个随时间变化的值,而不是一系列事件发生。因此,将其视为Behavior会更合适。

不幸的是,行为没有提供任何关于他们什么时候会改变的信息,都是“仅限于轮询”。现在,我可以尝试实现各种聪明的方案,最大限度地减少轮询,从而允许GUI元素的有效更新。 (Conal在original paper中做了类似的事情。)但是我采用了一种“没有魔力”的理念:图书馆用户应负责通过事件本身管理更新。

我目前设想的解决方案是提供一个第三类型除了EventBehavior,即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

+0

由于纯粹的操作/实现原因,我很抱歉看到语义模型和接口变得更加复杂。 – Conal

+0

如果你坚持使用这种新的实现驱动类型,我建议不要使用名称“Reactive”,因为与我在* [Push-pull functional reactive programming]中使用的语义不一致(http://conal.net/papers/push-pull-frp /)*可能会导致混淆。 – Conal

+0

随着时间的推移直接更新UI的价值无论如何可以说是概念上的不匹配。更新关心更改,而不是值。在我对Conal的回答的评论中概述的动机是,我发现系统中的许多元素在被视为一种派生类型时具有更清晰的语义。实际上应用更新可能会进一步包括自上次UI更新以来的时间段内求和导数,但这是一个明确的积分,并且在概念上与原始函数不同。 –