反应性香蕉中的动态事件切换导致严重泄漏

时间:2013-03-13 20:41:41

标签: haskell frp reactive-banana

我不确定这种行为是否是预期的(即我误用了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的堆配置文件:leaking memory 我反复切换事件;两个最大的峰值可能是事件的第二十次和第二十一次发射。

使用trimB感觉有点像挥手让这些类型加起来;我不知道我是正确使用它还是以某种方式滥用它。

我的子问题是:

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

2)我应该在不使用动态事件切换的情况下执行此操作吗?使用apply不会给出正确的行为,因为当基础行为发生更改时,不会触发生成的事件。如果我将所有三个输入解包到事件中,我想我可以设置一个折叠,手动累积每个输入事件的最新值。这是正确的方法吗?

1 个答案:

答案 0 :(得分:3)

如果您使用Behavior作为选择输入并回想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仍然是非常实验性的,其性能特征正在改善。)