在FRP中应用行为(和其他类型)的位置

ehi*_*ird 17 haskell frp reactive-programming

我正在研究一个使用反应性香蕉的程序,我想知道如何使用基本的FRP构建块来构建我的类型.

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

我本可以有

newtype Widget = Widget { widgetText :: Behavior String }
Run Code Online (Sandbox Code Playgroud)

但我也可以

newtype Widget = Widget { widgetText :: String }
Run Code Online (Sandbox Code Playgroud)

并使用Behavior Widget时,我想谈谈随时间变化的行为.这似乎使事情"更简单",并且意味着我可以Behavior更直接地使用操作,而不是必须解压缩和重新打包Widgets来执行它.

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

作为另一个例子,使用两种表示形式,有一个Monoid实例是有意义的(我希望在我的程序中有一个实例),但后者的实现似乎更自然(因为它只是将列表monoid轻微提升到newtype ).

(我的实际程序使用Discrete而不是Behavior,但我不认为这是相关的.)

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

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

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

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

dfl*_*str 6

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

  1. 尽可能地隔离"变化的东西".
  2. 将"同时改变的事物"组合成一个Behavior/ Event.

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

这样做的原因是Monoid,如您所述,可以重复使用原始类型的实例.

请注意,您可以使用镜头轻松修改数据类型的"内容",就像它们是原始值一样,因此额外的"包装/展开"不是问题.(有关特定镜头实现的介绍,请参阅此最新教程 ;还有其他内容)

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

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


Hei*_*mus 5

我同意dflemstr的建议,以

  1. 尽可能地隔离"变化的东西".
  2. 将"同时变化的东西"组合成一个Behavior/Event.

并且想为这些经验法则提供其他理由.

问题可归结为以下内容:您希望表示一对(元组)值随时间变化,问题是是否使用

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

Behavior (x,y) - 对的行为

优先选择其中一个的原因是

  • 超过b.

    在推送驱动的实现中,行为的更改将触发重新计算依赖于它的所有行为.

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

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

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

  • b超过.

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

    counters :: Behavior [Int]
    
    Run Code Online (Sandbox Code Playgroud)

    因为还没有办法跟踪动态的行为集合.也就是说,反应香蕉的下一个版本将包括动态事件切换.

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

    uncurry (liftA2 (,)) :: (Behavior a, Behavior b) -> Behavior (a,b)
    
    Run Code Online (Sandbox Code Playgroud)