反应性香蕉可以在网络中处理循环吗?

时间:2011-10-21 13:53:27

标签: haskell reactive-programming reactive-banana

我们有这样的代码:

 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状态直到达到平衡然后 它返回Nothing,因此它应该停止循环。一个新的模型改变 当然可以再次开始。

但是当我们将它们组合在一起时,我们会遇到无限循环 编译函数调用。即使autoLayout = Nothing,它仍然会在编译期间导致堆栈溢出。

如果我在guiState中删除了union调用并删除了evtAutoLayout 图片......

 guiState :: Discrete GuiState
 guiState = stepperD (GuiState []) $ mkGuiState <$> changes model

它工作正常。

有什么建议吗?

1 个答案:

答案 0 :(得分:15)

问题

  

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

不仅有一个,而且有三个答案。简短的答案是:1。一般不,2。有时是,3。有解决方法是。

这里的答案很长。

  1. reactive-banana的语义不支持直接用定义 Event

    这是Conal Elliott在其原始FRP语义中做出的决定,我决定坚持下去。它的主要好处是语义仍然非常简单,你可以随时考虑

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

    我提供了一个模块Reactive.Banana.Model,它几​​乎可以精确地实现这个模型,你可以查询它的源代码,以获得有关反应性香蕉语义的任何问题。特别是,您可以使用它来推断您的示例:使用笔和笔进行计算。论文或在GHCi中尝试(带有一些模拟数据)将告诉您值evtAutoLayout等于_|_,即未定义。

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

  2. 可以并鼓励根据再次引用该事件的Event来定义Behavior。换句话说,只要您通过行为,就允许递归。

    一个简单的例子是

    import Reactive.Banana.Model
    
    filterRising :: (FRP f, Ord a) => Event f a -> Event f a
    filterRising eInput = eOutput
        where
        eOutput  = filterApply (greater <$> behavior) eInput
        behavior = stepper Nothing (Just <$> 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)]
    

    给定事件流,函数filterRising仅返回大于先前返回的事件。这是在documentation for the stepper function中暗示的。

    然而,这可能不是你想要的那种递归。

  3. 仍然可以在反应香蕉中插入小延迟,它只是核心库的一部分,因此没有任何保证的语义。此外,您需要事件循环的一些支持才能做到这一点。

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

  4. 希望有所帮助;随时可以要求澄清,例子等。

    披露:我是反应香蕉库的作者。