2011-12-23 42 views
16

假設我有一個事件觸發器我想在觸發時做兩件事情。首先,我想讓它更新一些行爲的值。其次,如果滿足其他條件,我想讓它觸發另一個事件send_off更新的行爲值。用代碼形式表示,假設我有reactive-banana:包含行爲的最新值的射擊事件

trigger :: Event b 
trigger = ... 

updateFromTrigger :: b -> (a -> a) 
updateFromTrigger = ... 

conditionFromTrigger :: b -> Bool 
conditionFromTrigger = ... 

behavior :: Behavior a 
behavior = accumB initial_value (updateFromTrigger <$> trigger) 

send_off :: Event a 
send_off = ?????? (filterE conditionFromTrigger trigger) 

然後問題是:我在什麼位置?以便send_off發送最新的值爲的行爲,其中我的意思是包含從更新的值觸發剛剛應用到它。

不幸的是,如果我理解正確,行爲的語義是這樣的,更新後的值不是立即可用的,所以我唯一的選擇基本上是複製工作並重新計算更新後的行爲值,可以在另一個事件中立即使用它,即填寫?喜歡的東西

send_off = 
    flip updateFromTrigger 
    <$> 
    behavior 
    <@> 
    filterE conditionFromTrigger trigger 

現在,有我在其中可以使用,而不是行爲離散製造中的行爲提供給我更新的信息,馬上從某種意義上說,但實際上這只是相當於給我一個與我的原始事件同時被更新的價值的事件,除非我錯過了一些反應 - 香蕉不會讓我有辦法在兩個其他事件同時發射時才發射事件;也就是說,它提供了事件的聯合而不是交叉點。

所以我有兩個問題。首先,我對這種情況的理解是否正確,特別是我的結論是否正確:我的解決方案是解決問題的唯一方法?其次,純粹出於好奇,開發商是否有任何想法或計劃來處理事件的交叉點?

回答

6

優秀的問題!

不幸的是,我認爲這裏有這個基本問題沒有簡單的解決方案。問題如下:您需要最近的累計值,但trigger可能同時包含發生事件(仍然有序)。然後,

哪個同時累加器更新將是最近?

重點在於更新是在它們屬於的事件流中排序的,但與其他事件流沒有關係。此處使用的FRP語義不再知道哪個同時更新到behavior對應於哪個同時發生的事件send_off。特別是,這表明您提議的send_off實施可能不正確;當trigger包含同時發生的事件時它不起作用,因爲行爲可能會多次更新,但是您只重新計算一次更新。記住

有了這個,我能想到的幾種方法的問題:

  1. 使用mapAccum註釋與最新更新的累加值每個觸發事件。

    (trigger', behavior) = mapAccum initial_value $ f <$> trigger 
        where 
        f x acc = (x, updateFromTrigger acc) 
    
    send_off = fmap snd . filterE (conditionFromTrigger . fst) $ trigger' 
    

    我認爲這個解決方案在模塊化方面缺乏一點,但鑑於上面的討論,這可能很難避免。

  2. Discrete重新鑄造所有物件。

    我在這裏沒有任何具體的建議,但它可能是你的send_off事件感覺更像是一個值的更新,而不是像一個適當的事件。在這種情況下,可能需要將所有事件都按照Discrete進行投放,其中Applicative實例在發生同時事件時確實是「正確的事情」。

    以類似的精神,我經常使用changes . accumD而不是accumE,因爲它感覺更自然。

  3. 功香蕉(> 0.4.3)的下一個版本將可能包括功能

    collect :: Event a -> Event [a] 
    spread :: Event [a] -> Event a 
    

    該具體化,RESP。反映同時事件。無論如何,我需要他們優化Discrete類型,但它們對於像現在的問題這樣的東西可能也很有用。

    特別是,他們將允許您定義正是如此事件的交集:

    intersect :: Event a -> Event b -> Event (a,b) 
    intersect e1 e2 
         = spread . fmap f . collect 
         $ (Left <$> e1) `union` (Right <$> e2) 
        where 
        f xs = zipWith (\(Left x) (Right y) -> (x,y)) left right 
         where (left, right) = span isLeft xs 
    

    然而,在上面的討論,這個功能可能會比你希望它是那麼有用。特別是,它不是唯一的,有很多變體。

+0

任何時候我們可能會期待更新的意義? :-) –

+0

@GregoryCrosswhite:你一直在問很好的問題。 ;-)我爲新的內部構思設計了一個漂亮的設計,但實現它需要一段時間。 –