反应性香蕉中的动态事件切换导致严重泄漏 [英] Dynamic event switching in reactive-banana causes severe leak
问题描述
我不确定这种行为是否是预期的(即我滥用了Reactive.Banana.Switch)或一个bug。
假设我有两个像-type的输入行为,我想根据事件在它们之间切换。我写了这个函数:
switchBehaviors ::
行为t a - |最初和真实事件发生后的行为
- >行为t a - |在假事件之后产生的行为
- >事件t布尔 - |在行为之间进行选择
- >矩t(行为ta)
switchBehaviors tf es = do
t'< - trimB t
f'< - trimB f
return $ switchB t $(\e - > if e then t'else f')< $> es
这段代码看起来很奇怪,它嵌入到一个简单的GUI模型中时进行类型检查,编译并提供期望的结果。 (行为的两个文本输入字段,发出交替真和假事件的按钮,以及使用 sink
绑定到组合行为的标签。)
然而,在多次触发事件后,很明显在某处发生灾难性泄漏。该应用程序开始花费越来越多的时间来对输入行为和新事件的变化做出反应。它也开始进食记忆。
这里有一个堆配置文件,带-hC:
我不断地切换事件;这两个最大的峰值可能是该赛事的第20次和第21次击球。
使用trimB感觉有点像挥手让这些类型加起来;我不知道我是否正确使用它或以某种方式滥用它。
我的子问题是:
1)我是滥用Reactive.Banana.Switch API,还是这是一个错误?如果我滥用API,我做错了什么?
2)我应该不使用动态事件切换吗?使用 apply
并没有给出正确的行为,因为当底层行为改变时,结果事件不会触发。如果我将所有三个输入展开到事件,我想我可以设置一个折叠,手动累积每个输入事件的最新值。这是正确的方法吗?
如果您使用 Behavior
作为选择输入并回顾 Applicative
的一个实例。我没有真正内化 f< $> x * y * z ...
习惯用法,当我问这个问题时,所以这里是一个明确的解决方案,像我这样的其他人:
switchBehaviors
::行为ta - |行为在真时产生
- >行为t a - |行为在假时产生
- >行为t Bool - |在行为之间进行选择
- >行为t a
switchBehaviors t f es =
(\e x y - >如果e然后x else y)< $> es< *> t * f
(Heinrich Apfelmus在评论中提到了第一个问题,正如他注意到的那样, Reactive.Banana.Switch
仍然是非常实验性的,它的性能特征正在改善。)
I'm not sure whether this behavior is expected (i.e. I'm misusing Reactive.Banana.Switch) or a bug.
Let's say I have two like-typed input Behaviors, and I want to switch between them based on an Event. I wrote this function:
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
This code seems innoccuous enough; it type-checks, compiles, and gives the desired result when embedded into a simple GUI mockup. (Two text entry fields for the Behaviors, a button emitting alternate True and False Events, and a label bound to the combined Behavior using sink
.)
However, after triggering the Event several times, it becomes obvious that there's a catastropic leak somewhere. The app starts taking longer and longer to react both to changes in the input Behaviors and to new Events. It also starts eating memory.
Here's a heap profile with -hC: I'm repeatedly toggling the Event; the two largest spikes are maybe the twentieth and twenty-first firings of the Event.
The use of trimB feels a bit like hand-waving to make the types add up; I don't know whether I'm using it correctly or abusing it somehow.
My sub-questions are:
1) Am I abusing the Reactive.Banana.Switch API, or is this a bug? If I am abusing the API, what am I doing wrong?
2) Should I do this without using dynamic event switching? Using apply
doesn't give the correct behavior, because the resulting Event doesn't fire when the underlying Behavior changes. If I unwrap all three inputs to Events, I imagine I can set up a fold, manually accumulating the most recent value of each input Event. Is that the correct approach?
This behavior is actually trivial to implement without dynamic switching, if you use a Behavior
for the selection input and recall that Behavior
is an instance of Applicative
. I hadn't really internalized the f <$> x <*> y <*> z ...
idiom when I asked this question, so here's an explicit working-out for others like me:
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 addressed the first question in a comment. As he notes, Reactive.Banana.Switch
is still very much experimental, and its performance characteristics are improving.)
这篇关于反应性香蕉中的动态事件切换导致严重泄漏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!