17

我正在使用reactive-banana开发一个程序,我想知道如何使用基本的 FRP 构建块来构建我的类型。

例如,这是我真实程序中的一个简化示例:假设我的系统主要由小部件组成——在我的程序中,是随时间变化的文本片段。

我本可以有

newtype Widget = Widget { widgetText :: Behavior String }

但我也可以

newtype Widget = Widget { widgetText :: String }

Behavior Widget在我想谈论随时间变化的行为时使用。这似乎让事情变得“更简单”,并且意味着我可以Behavior更直接地使用操作,而不必解包和重新打包小部件来完成它。

另一方面,前者似乎避免了实际定义小部件的代码中的重复,因为几乎所有小部件都随时间而变化,而且我发现自己甚至定义了少数不带的小部件Behavior,因为它让我可以将它们与其他人以更一致的方式。

另一个例子,对于这两种表示,有一个实例是有意义的Monoid(我想在我的程序中有一个),但后者的实现似乎更自然(因为它只是将 list monoid 提升到 newtype )。

(我的实际程序使用Discrete而不是Behavior,但我认为这无关紧要。)

同样,我应该使用Behavior (Coord,Coord)还是(Behavior Coord, Behavior Coord)表示 2D 点?在这种情况下,前者似乎是显而易见的选择;但当它是代表游戏中实体之类的五元素记录时,选择似乎不太明确。

本质上,所有这些问题都归结为:

使用 FRP 时,我应该在哪一层应用Behavior类型?

(同样的问题也适用Event,尽管程度较轻。)

4

2 回答 2

6

我在开发 FRP 应用程序时使用的规则是:

  1. 尽可能隔离“变化的事物”。
  2. 将“同时发生变化的事物”分组为一个Behavior/ Event

(1) 的原因是,如果您使用的数据类型尽可能原始,则创建和组合抽象操作会变得更容易。

这样做的原因是Monoid,如您所描述的,可以将诸如此类的实例用于原始类型。

请注意,您可以使用Lenses轻松修改数据类型的“内容”,就好像它们是原始值一样,因此额外的“包装/解包”基本上不是问题。(有关此特定 Lens 实现的介绍,请参阅此最近的教程;还有其他的)

(2) 的原因是它只是消除了不必要的开销。如果两件事同时发生变化,则它们“具有相同的行为”,因此应该对它们进行建模。

Ergo/ tl;dr:你应该使用newtype Widget = Widget { widgetText :: Behavior String }因为(1),你应该使用Behavior (Coord, Coord)因为(2)(因为两个坐标通常同时变化)。

于 2011-12-21T22:25:10.290 回答
5

我同意dflemstr 的建议

  1. 尽可能隔离“变化的事物”。
  2. 将“同时发生变化的事物”归为一组Behavior/Event

并想为这些经验法则提供更多理由。

问题归结为以下几点:您想表示一对(元组)随时间变化的值,问题是是否使用

一个。(Behavior x, Behavior y)- 一对行为

湾。Behavior (x,y)- 对的行为

偏爱其中之一的原因是

  • a 超过 b

    在推送驱动的实现中,行为的改变会触发所有依赖它的行为的重新计算。

    现在,考虑一个行为,其值仅取决于该x对的第一个组件。在变体a中,第二个组件的更改y不会重新计算行为。但是在变体b中,将重新计算行为,即使它的值根本不依赖于第二个组件。换句话说,这是一个细粒度与粗粒度依赖关系的问题。

    这是建议 1 的论据。当然,当两种行为倾向于同时改变时,这并不重要,这会产生建议 2。

    当然,库应该提供一种方法来提供细粒度的依赖关系,即使对于变体b也是如此。从响应式香蕉版本 0.4.3 开始,这是不可能的,但暂时不要担心,我的推送驱动实现将在未来版本中成熟。

  • b 超过 a

    看到 reactive-banana 版本 0.4.3 还没有提供动态事件切换,有些程序只有在你将所有组件放在一个行为中时才能编写。规范示例是具有可变数量计数器的程序,即TwoCounter.hs示例的扩展。您必须将其表示为随时间变化的值列表

    counters :: Behavior [Int]
    

    因为目前还没有办法跟踪动态的行为集合。也就是说,reactive-banana 的下一个版本将包括动态事件切换。

    此外,您始终可以毫无问题地从变体a转换为变体b

    uncurry (liftA2 (,)) :: (Behavior a, Behavior b) -> Behavior (a,b)
    
于 2011-12-24T09:42:38.403 回答