2011-10-21 53 views
15

我們有這樣的代碼:反應式香蕉能否在網絡中處理週期?

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 

它工作正常。

有什麼建議嗎?

回答

15

問題

是否反應香蕉庫支持遞歸定義的事件?

不僅有一個,而且有三個答案。簡短的答案是:1.一般不會,2.有時是,3.解決方法是。

這裏的答案很長。

  1. 反應香蕉的語義做不支持直接的本身來定義一個Event

    這是Conal Elliott在他最初的FRP語義中做出的決定,我決定堅持下去。它的主要好處是,語義仍然非常簡單,你可以一直認爲在

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

    我提供了一個模塊Reactive.Banana.Model實現幾乎正是這個模型而言,可以諮詢其源代碼有關的語義有任何疑問的反應香蕉。特別是,你可以用它來推論你的例子:使用筆&紙或者在GHCi中嘗試它(帶有一些模擬數據)會告訴你值evtAutoLayout等於_|_,即未定義。

    後者可能會令人驚訝,但是如您所寫,該示例確實未定義:GUI狀態僅在事件發生時纔會更改,但只有在知道GUI狀態是否更改時纔會發生,等等。你總是需要打破插入一個小延遲 strangulating反饋循環。不幸的是,反應香蕉目前還沒有提供插入小延遲的方法,主要是因爲我不知道如何按照允許遞歸的方式描述[(Time,a)]模型的小延遲。 (但請參閱答案3。)

  2. 根據再次引用該事件的Behavior定義Event是可能的並且被鼓勵。換句話說,只要你經歷一個行爲,就允許遞歸。

    一個簡單的例子是

    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的遞歸使用。我不太清楚將定時器時間間隔設置爲0時會發生什麼,但它可能執行得太早。您可能需要嘗試一下才能找到一個好的解決方案。

希望有幫助;隨時要求澄清,例子等。

披露:我是反應性香蕉圖書館的作者。

+0

既然你說我可以要求澄清/例子......在你的filterRising中,第一個參數是什麼?如果只是將事件轉換爲事件,爲什麼它有2個參數? 你將如何使用filterRising?謝謝! – mentics

+0

@taotree:啊,第一個參數只是某種初始值。我現在已經改變了示例以匹配描述。使用'filterRising'函數很簡單:它將一個事件流作爲參數,並返回一個新的事件流,所以你可以將它應用到你選擇的事件流並獲得一個新的事件流。 –