反应式香蕉可以处理网络中的循环吗?

发布于 2024-12-11 03:09:40 字数 709 浏览 0 评论 0原文

我们有这样的代码:

 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

阿楠 2024-12-18 03:09:40

问题

reactive-banana 库是否支持递归定义的事件?

答案不仅只有一个,还有三个。简短的回答是:1.通常不会,2.有时会,3.有解决方法是。

这是长答案。

  1. reactive-banana 的语义不支持直接根据自身定义事件

    这是 Conal Elliott 在他最初的 FRP 语义中做出的决定,我决定坚持下去。它的主要好处是语义仍然非常简单,您始终可以根据

    类型行为 a = 时间 ->一个
    输入事件 a = [(时间,a)]
    

    我提供了一个模块响应式。 Banana.Model 几乎精确地实现了该模型,您可以查阅其源代码以了解有关reactive-banana语义的任何问题。特别是,您可以使用它来推理您的示例:使用笔和笔进行计算论文或在 GHCi 中尝试(使用一些模拟数据)会告诉您值 evtAutoLayout 等于 _|_,即未定义。

    后者可能令人惊讶,但正如您所写,该示例确实未定义:GUI 状态仅在 evtAutoLayout 事件发生时才会发生变化,但只有当您知道 GUI 是否状态变化,反过来等等。您总是需要通过插入一个小的延迟来打破扼杀的反馈循环。不幸的是,reactive-banana 目前没有提供插入小延迟的方法,主要是因为我不知道如何用 [(Time,a)] 模型来描述小延迟允许递归的方式。 (但请参阅答案 3。)

  2. 可以并鼓励根据再次引用事件的行为来定义事件。换句话说,只要经历了一个Behavior,就允许递归。

    一个简单的例子是

    导入Reactive.Banana.Model
    
    filterRising :: (FRP f, Ord a) =>事件fa->事件发
    过滤器上升 eInput = eOutput
        在哪里
        eOutput = filterApply(更大的<
    gt;行为)eInput
        行为=步进器什么都没有(只是<
    gt; eOutput)
    
        更大的无 _ = True
        更大(仅x) y = x < y
    
    示例 :: [(时间,整数)]
    示例=解释时间filterRising $ zip [1..] [2,1,5,4,8,9,7]
    -- 示例 = [(1.0, 2),(3.0, 5),(5.0, 8),(6.0, 9)]
    

    给定一个事件流,函数 filterRising 仅返回那些大于先前返回的事件。 中暗示了这一点stepper 函数 的文档。

    但是,这可能不是您想要的递归类型。

  3. 尽管如此,仍然可以在reactive-banana中插入小的延迟,它只是不是核心库的一部分,因此不具有任何有保证的语义。此外,您确实需要事件循环的一些支持才能做到这一点。

    例如,您可以使用 wxTimer 安排一个事件在您处理完当前事件后立即发生。 Wave.hs 示例演示了递归使用带有reactive-banana 的wxTimer。我不太清楚当您将计时器间隔设置为 0 时会发生什么,但它可能执行得太早。您可能需要进行一些尝试才能找到一个好的解决方案。

希望有帮助;请随意要求说明、示例等。

披露:我是reactive-banana 库的作者。

The question

Does the reactive-banana library support recursively defined events?

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.

  1. 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

    type Behavior a = Time -> a
    type Event    a = [(Time,a)]
    

    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.)

  2. It is possible and encouraged to define an Event in terms of a Behavior 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

    import Reactive.Banana.Model
    
    filterRising :: (FRP f, Ord a) => Event f a -> Event f a
    filterRising eInput = eOutput
        where
        eOutput  = filterApply (greater <
    gt; behavior) eInput
        behavior = stepper Nothing (Just <
    gt; eOutput)
    
        greater Nothing  _ = True
        greater (Just x) y = x < y
    
    example :: [(Time,Int)]
    example = interpretTime filterRising $ zip [1..] [2,1,5,4,8,9,7]
    -- example = [(1.0, 2),(3.0, 5),(5.0, 8),(6.0, 9)]
    

    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 the stepper function.

    However, this is probably not the kind of recursion you desire.

  3. 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文