我不确定这种行为是预期的(即我误用了 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
绑定(bind)到组合行为的标签。)
但是,在多次触发该事件后,很明显某处存在灾难性泄漏。应用程序开始花费越来越长的时间来对输入行为的变化和新事件使用react。它还开始吞噬内存。
这是带有 -hC 的堆配置文件: 我反复切换事件;两个最大的峰值可能是该事件的第二十次和第二十次发射。
使用trimB感觉有点像挥手让类型加起来;我不知道我是否正确使用它或以某种方式滥用它。
我的子问题是:
1) 我是否滥用了 Reactive.Banana.Switch API,或者这是一个错误?如果我滥用 API,我做错了什么?
2) 我应该在不使用动态事件切换的情况下执行此操作吗?使用 apply
不会给出正确的行为,因为当底层行为发生更改时,生成的事件不会触发。如果我打开事件的所有三个输入,我想我可以设置一个折叠,手动累积每个输入事件的最新值。这是正确的方法吗?
最佳答案
如果您使用 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
仍处于实验阶段,其性能特征正在改进。)
关于haskell - 响应式(Reactive)香蕉中的动态事件切换导致严重泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15396038/