3

我不确定这种行为是预期的(即我误用了 Reactive.Banana.Switch)还是一个错误。

假设我有两个类似类型的输入行为,我想根据一个事件在它们之间切换。我写了这个函数:

switchBehaviors :: 
      Behavior t a -- | Behavior to yield initially and after "True" events
   -> Behavior t a -- | Behavior to yield after "False" events
   -> Event t Bool -- | Select between behaviors
   -> Moment t (Behavior t a)
switchBehaviors t f es = do
    t' <- trimB t
    f' <- trimB f
    return $ switchB t $ (\e -> if e then t' else f') <$> es

这段代码似乎无关紧要;当嵌入到简单的 GUI 模型中时,它会进行类型检查、编译并给出所需的结果。(行为的两个文本输入字段,一个发出交替 True 和 False 事件的按钮,以及一个使用 绑定到组合行为的标签sink。)

然而,在多次触发事件后,很明显某处发生了灾难性的泄漏。应用程序开始花费越来越长的时间来对输入行为的变化和新事件做出反应。它也开始吞噬记忆。

这是带有 -hC 的堆配置文件:泄漏内存 我反复切换事件;两个最大的峰值可能是事件的第 20 次和第 21 次触发。

使用 trimB 感觉有点像挥手让类型相加;我不知道我是否正确使用它或以某种方式滥用它。

我的子问题是:

1) 我是在滥用 Reactive.Banana.Switch API,还是这是一个错误?如果我滥用 API,我做错了什么?

2)我应该在不使用动态事件切换的情况下这样做吗?Usingapply不会给出正确的行为,因为当底层行为发生变化时,结果事件不会触发。如果我打开事件的所有三个输入,我想我可以设置一个折叠,手动累积每个输入事件的最新值。这是正确的方法吗?

4

1 回答 1

3

如果您使用 aBehavior作为选择输入并调用它BehaviorApplicative. 当我问这个问题时,我并没有真正内化这个f <$> x <*> y <*> z ...成语,所以这里有一个明确的锻炼方法,适合像我这样的其他人:

switchBehaviors 
    :: Behavior t a    -- | Behavior to yield when it's "True"
    -> Behavior t a    -- | Behavior to yield when it's "False"
    -> Behavior t Bool -- | Select between behaviors
    -> Behavior t a
switchBehaviors t f es = 
    (\e x y -> if e then x else y) <$> es <*> t <*> f

(Heinrich Apfelmus 在评论中回答了第一个问题。正如他所指出的,Reactive.Banana.Switch它仍处于试验阶段,其性能特征正在改进。)

于 2013-03-16T19:17:50.290 回答