簡體   English   中英

FRP中的“行為現在”

[英]“Behavior now” in FRP

在先前的SO問題( 是否有可能?:行為T [行為TA] - >行為T [A] ),我們分析的存在Behavior join (使用reactive-banana而言)。

Behavior t (Behavior t a) -> Behavior t a

在語義模型中實現如下

type Behavior t a = t -> a

behaviorNow :: Behavior t (Behavior t a) -> Behavior t a
behaviorNow f t = f t t

雖然實施這一直接將是不幸的,因為我們可以產生Behavior Monad使用constbehaviorNow ,如果和如何behaviorNow違反FRP的語義?

我很樂意使用任何其他FRP系統的術語來聽取答案,如果有意義的話,還要進行比較。

在基於輪詢的FRP系統中,任何行為都具有有意義的join

  • join bb的樣本是通過采樣bb獲得的b的樣本

在基於推送的FRP中,任何由其他步驟函數組成的步進函數的行為都具有有意義的>>=join 通過>>=推送值可以用命令性的術語來描述:

  • 當綁定的參數發生變化時,評估綁定和
    • 將當前步驟功能更改為返回的步驟功能
    • 將值更改為當前步驟函數的值
  • 當當前步進功能的值發生變化時,請更改該值

提供Monad實例可能稍微不合需要,因為庫用戶可能會優先選擇它,即使效率較低。 例如,當使用>>=構建計算時, 此無關答案中的代碼執行的工作量大於使用<*>等效構建的計算。

Conal Elliott用聲明術語描述了一個join用於同時推送和輪詢從步驟函數構建的行為的值:

-- Reactive is a behavior that can only be a step function
data Reactive a = a `Stepper` Event a
newtype Event a = Ev (Future (Reactive a))

join :: Reactive (Reactive a) -> Reactive a
join ((a `Stepper` Ev ur) `Stepper` Ev urr ) =
    ((`switcher` Ev urr ) <$> ur) _+_ (join <$> urr )

switcher :: Reactive a -> Event (Reactive a) -> Reactive a
r `switcher` er = join (r `Stepper` er)

其中Future是我們還沒有看到的值的類型, _+_Future發生的兩種可能性中的第一種,而<$>Future s上的infix fmap [1]

如果我們不提供任何其他創造行為的方法

  • 常數函數(通常是階梯函數)
  • 一個“步進器”,記住事件的最新價值
  • 在組合器本身不隨時間變化的情況下應用各種行為組合器

然后每個行為都是一個階梯函數,我們可以使用這個或類似的Monad實例進行行為。

只有當我們希望行為是連續的或者是事件發生之外的某個時間的函數時,才會出現困難。 考慮一下我們是否有以下內容

time :: Behavior t t

這是跟蹤當前時間的行為。 用於輪詢系統的Monad實例仍然是相同的,但我們無法再通過系統可靠地推送更改。 當我們做出像time >>= \\x -> if am x then return 0 else return 1一樣簡單的事情時會發生什么time >>= \\x -> if am x then return 0 else return 1am t是真的)? 我們對>>= above和Elliot join定義都不能承認知道何時變化的優化; 它不斷變化。 我們能做的最好>>=類似於:

  • 如果我們知道綁定的參數是步進值的話
    • 當綁定的參數發生變化時,評估綁定和
      • 將當前步驟功能更改為返回的步驟功能
      • 將值更改為當前步驟函數的值
    • 當當前步進功能的值發生變化時,請更改該值
  • 除此以外
    • 返回此>>=的抽象語法樹

對於join的形式,我們將減少到做類似的東西,只是記錄了AST的情況下,在一個外部行為join不是一個階躍函數。

此外,使用此作為輸入構建的任何內容都可能在中午和午夜發生變化,無論是否引發任何其他事件。 從那時起它就會玷污一切,無法可靠地推動事件。

從實現的角度來看,我們最好的選擇似乎是不斷輪詢time ,並用從輪詢事件構建的步進器替換它所使用的任何地方。 這不會更新事件之間的值,因此現在我們庫的用戶無法可靠地輪詢值。

我們對實現的最佳選擇是保留抽象語法樹,了解這些任意行為所發生的事情,並且不提供從行為生成事件的方法。 然后可以輪詢行為,但不會為它們推送任何更新。 在這種情況下,我們不妨將它從庫中刪除,並讓用戶傳遞AST(他們可以Free獲得),並讓用戶在每次輪詢時評估整個AST。 我們無法再為庫用戶優化它,因為這樣的任何值都可以不斷變化。

有一個最終選項,但它涉及引入相當多的復雜性。 介紹連續變化值的屬性的可預測性概念和連續變化值的計算。 這將允許我們為更大的時變行為子集提供Monad接口,但不能為所有這些行為提供。 這種復雜性在程序的其他部分已經是可取的,但我不知道符號數學之外的任何庫試圖解決這個問題。

(作者在這里。)

首先請注意, behaviorNow函數是monadic join

在反應性香蕉-0.7中, Behavior t不是因為效率會產生嚴重后果的因素。

第一個也是最重要的問題是行為也可以是有狀態的。 join一起,這將導致時間泄漏 問題的主要指示是內部Behavior t開始時間 t與外部Behavior t相同。 例如,考慮該計划

e  :: Event t Int
b  :: Int -> Behavior t Int
b x = accumB 0 $ (x+) <$ e

bb :: Behavior t (Behavior t Int)
bb = stepper (pure 0) $ b <$> e

行為join bb需要跟蹤事件e的整個歷史記錄,以便在b的定義中執行累積。 換句話說,事件e永遠不會被垃圾收集 - 時間泄漏。

第二個問題是在內部, Behavior t的實現還包括跟蹤行為何時發生變化的事件。 然而,一個自由使用的join組合子,例如作為暗示的do記號,會導致頗為曲折的計算,以確定該行為是否有變化。 這與首先​​保持跟蹤的原因相反:通過避免昂貴的計算來提高效率。


Reactive.Banana.Switch模塊提供各種組合器,它們是join功能的同類,但避免了巧妙選擇類型的第一個問題。 特別是:

  • switchB功能是最直接的join模擬。
  • AnyMoment Identity類型與Behavior類型類似,但沒有狀態且沒有跟蹤更改。 因此,它有一個monad實例。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM