我不确定这种行为是否是预期的(即我误用了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的堆配置文件: 我反复切换事件;两个最大的峰值可能是事件的第二十次和第二十一次发射。
使用trimB感觉有点像挥手让这些类型加起来;我不知道我是正确使用它还是以某种方式滥用它。
我的子问题是:
1)我是否滥用Reactive.Banana.Switch API,或者这是一个错误?如果我滥用API,我做错了什么?
2)我应该在不使用动态事件切换的情况下执行此操作吗?使用apply
不会给出正确的行为,因为当基础行为发生更改时,不会触发生成的事件。如果我将所有三个输入解包到事件中,我想我可以设置一个折叠,手动累积每个输入事件的最新值。这是正确的方法吗?
答案 0 :(得分:3)
如果您使用Behavior
作为选择输入并回想Behavior
是Applicative
的实例,则无需动态切换即可实现此行为。当我提出这个问题时,我没有真正内化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
仍然是非常实验性的,其性能特征正在改善。)