反应式香蕉可以处理网络中的循环吗?
我们有这样的代码:
guiState :: Discrete GuiState
guiState = stepperD (GuiState []) $
union (mkGuiState <$> changes model) evtAutoLayout
evtAutoLayout :: Event GuiState
evtAutoLayout = fmap fromJust . filterE isJust . fmap autoLayout $ changes guiState
您可以看到 evtAutoLayout 馈入 guiState 馈入 evtAutoLayout——所以那里有一个循环。这是故意的。汽车 布局调整gui状态直到达到平衡然后 它不返回任何内容,因此它应该停止循环。新的模式改变 当然可以再次开始。
然而,当我们把它们放在一起时,我们遇到了无限循环 编译函数调用。即使autoLayout = Nothing,仍然会导致编译期间堆栈溢出。
如果我删除 guiState 中的联合调用并删除 evtAutoLayout 图片……
guiState :: Discrete GuiState
guiState = stepperD (GuiState []) $ mkGuiState <$> changes model
效果很好。
有什么建议吗?
We have code like this:
guiState :: Discrete GuiState
guiState = stepperD (GuiState []) $
union (mkGuiState <gt; changes model) evtAutoLayout
evtAutoLayout :: Event GuiState
evtAutoLayout = fmap fromJust . filterE isJust . fmap autoLayout $ changes guiState
You can see that evtAutoLayout feeds into guiState which feeds into
evtAutoLayout--so there is a cycle there. This is deliberate. Auto
layout adjusts the gui state until it reaches an equilibrium and then
it returns Nothing and so it should stop the loop. A new model change
can kick it off again, of course.
When we put this together, though, we run into an infinite loop on the
compile function call. Even if autoLayout = Nothing, it still results in a stack overflow during compile.
If I remove the union call in guiState and remove evtAutoLayout out of
the picture...
guiState :: Discrete GuiState
guiState = stepperD (GuiState []) $ mkGuiState <gt; changes model
it works fine.
Any suggestions?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
问题
答案不仅只有一个,还有三个。简短的回答是:1.通常不会,2.有时会,3.有解决方法是。
这是长答案。
reactive-banana 的语义不支持直接根据自身定义
事件
。这是 Conal Elliott 在他最初的 FRP 语义中做出的决定,我决定坚持下去。它的主要好处是语义仍然非常简单,您始终可以根据
我提供了一个模块响应式。 Banana.Model 几乎精确地实现了该模型,您可以查阅其源代码以了解有关reactive-banana语义的任何问题。特别是,您可以使用它来推理您的示例:使用笔和笔进行计算论文或在 GHCi 中尝试(使用一些模拟数据)会告诉您值
evtAutoLayout
等于_|_
,即未定义。后者可能令人惊讶,但正如您所写,该示例确实未定义:GUI 状态仅在
evtAutoLayout
事件发生时才会发生变化,但只有当您知道 GUI 是否状态变化,反过来等等。您总是需要通过插入一个小的延迟来打破扼杀的反馈循环。不幸的是,reactive-banana 目前没有提供插入小延迟的方法,主要是因为我不知道如何用[(Time,a)]
模型来描述小延迟允许递归的方式。 (但请参阅答案 3。)可以并鼓励根据再次引用事件的
行为
来定义事件
。换句话说,只要经历了一个Behavior,就允许递归。一个简单的例子是
给定一个事件流,函数
filterRising
仅返回那些大于先前返回的事件。 中暗示了这一点stepper
函数 的文档。但是,这可能不是您想要的递归类型。
尽管如此,仍然可以在reactive-banana中插入小的延迟,它只是不是核心库的一部分,因此不具有任何有保证的语义。此外,您确实需要事件循环的一些支持才能做到这一点。
例如,您可以使用 wxTimer 安排一个事件在您处理完当前事件后立即发生。 Wave.hs 示例演示了递归使用带有reactive-banana 的wxTimer。我不太清楚当您将计时器间隔设置为
0
时会发生什么,但它可能执行得太早。您可能需要进行一些尝试才能找到一个好的解决方案。希望有帮助;请随意要求说明、示例等。
披露:我是reactive-banana 库的作者。
The question
has not only one, but three answers. The short answers are: 1. generally no, 2. sometimes yes, 3. with workaround yes.
Here the long answers.
The semantics of reactive-banana do not support defining an
Event
directly in terms of itself.This is a decision that Conal Elliott made in his original FRP semantics and I've decided to stick to it. Its main benefit is that the semantics remain very simple, you can always think in terms of
I have provided a module Reactive.Banana.Model that implements almost precisely this model, you can consult its source code for any questions concerning the semantics of reactive-banana. In particular, you can use it to reason about your example: a calculation with pen & paper or trying it in GHCi (with some mock data) will tell you that the value
evtAutoLayout
is equal to_|_
, i.e. undefined.The latter may be surprising, but as you wrote it, the example is indeed undefined: the GUI state only changes if an
evtAutoLayout
event happens, but it can only happen if you know whether the GUI state changes, which in turn, etc. You always need to break the strangulating feedback loop by inserting a small delay. Unfortunately, reactive-banana doesn't currently offer a way to insert small delays, mainly because I don't know how to describe small delays in terms of the[(Time,a)]
model in a way that allows recursion. (But see answer 3.)It is possible and encouraged to define an
Event
in terms of aBehavior
that refers to the Event again. In other words, recursion is allowed as long as you go through a Behavior.A simple example would be
Given an event stream, the function
filterRising
returns only those events that are greater than the previously returned. This is hinted at in the documentation for thestepper
function.However, this is probably not the kind of recursion you desire.
Still, it is possible to insert small delays in reactive-banana, it's just not part of the core library and hence doesn't come with any guaranteed semantics. Also, you do need some support from your event loop to do that.
For instance, you can use a wxTimer to schedule an event to happen right after you've handled the current one. The Wave.hs example demonstrates the recursive use of a wxTimer with reactive-banana. I don't quite know what happens when you set the timer interval to
0
, though, it might execute too early. You probably have to experiment a bit to find a good solution.Hope that helps; feel free to ask for clarifications, examples, etc.
Disclosure: I'm the author of the reactive-banana library.