函數式響應式編程
来自维基百科,自由的百科全书
函數式響應式編程(FRP) 是一種編程範式,它採用函數式編程的基礎部件(如map、reduce、filter等),進行響應式編程(異步數據流程編程)。FRP被用於GUI、機器人和音樂方面的編程,旨在通過顯式的建模時間來簡化這些問題。
FRP的形式
函數式響應編程,自從1997年由Conal Elliott和保羅·胡達客在ICFP 97論文《函數式響應式動畫》中提出以來[1],產生了多種形式。其多樣性的一條軸線,是離散與連續語義對比。另一條軸線,是怎樣讓FRP系統可以動態地更改。[2]
FRP的最早形式採用了連續的語義,旨在抽象出對程序的意義無關緊要的操作細節。[1]這種形式的關鍵屬性為:
- 建模在連續時間內變化的值,叫做「行為」,後來稱為「信號」。
- 建模「事件」,它發生在離散的時間點上。
- 系統可在響應事件時被改變,通用採用術語「切換」。
- 從響應模型中分離出求值細節,如採樣率。
這種FRP語義模型在無副作用的語言中通常採用隨時間變化的連續函數。[3]
如事件驅動FRP和Elm在0.17版本之前的那種形式,它們要求更新過程是離散的,且由事件驅動。[4]這些形式在FRP的實踐中被加以推崇,它們專注於擁有簡單API的語義,比如在機器人或Web瀏覽器中可以被高效地實現。[5]
在這些形式下,行為和事件的概念通常會被合併成信號,它總是擁有當前值,但會被離散地改變。[6]
交互式FRP
Conal Elliott在2008年已經指出,從輸入到輸出,這種普通的FRP模型,不太適合交互式程序。[7]在從輸入映射到輸出的過程中缺乏「運行」程序的能力,可能意味著必須使用以下解決方案之一:
- 創建表示行動的一個資料結構,它表現為輸出。行動必須被一個外部的解釋器或環境來運行。它繼承了最初Haskell的流式I/O的全部難點。[8]
- 使用箭頭化的FRP,和有能力實行行動的嵌入箭頭。行動也必須有身份標識,比如說使得它們可以做維護各自的可變存儲。採取這種辦法的是Fudgets庫[9],和更一般性的單子流函數[10]。
- 最新穎的方法就是允許行動現在運行(於IO單子中),但將它們結果的接收推遲到以後。[11]它利用了事件和IO單子之間的交互,並兼容於更加面向表達式的FRP:
planNow :: Event (IO a) -> IO (Event a)
實現問題
存在兩種類型的FRP系統,基於推送的和基於拉取的。基於推送的系統接收事件,並將它們推過一個信號網絡來達成結果。基於拉取的系統會等待對結果的需求,並逆向通過該網絡檢索所需求的值。
某些FRP系統例如Yampa使用採樣,這裡將採樣推過一個信號網絡。這種方法有個缺點:網絡在一個計算步驟持續期間必須等待,然後才能發現輸入上的變更。採樣就是個基於推送的FRP示例。
Hackage上的Reactive和Etage庫介入了一種叫做「推送-拉取FRP」的方式。按照這種方式,只在需求純粹定義的流(如隨時間推移的固定事件的列表)上的下一個事件時,才會構造該事件。這些純粹定義的流的行為類似於Haskell中的惰性列表。此為基於拉取的部分。基於推送的部分會在系統外部的事件被帶入系統時使用到。外部的事件會被推送給消費者,這樣它們就可以在它發布的瞬間找到該事件。
實現
- Yampa[12],是一個箭頭化、高效的、純Haskell實現,支持SDL、SDL2、OpenGL和HTML DOM。
- reflex[13],是一個高效的,用Haskell實現的推送/拉取式FRP,主要宿主是瀏覽器/DOM、SDL和Gloss。
- reactive-banana[14],是一個用Haskell實現的目標不可知的推送式FRP。
- netwire[15]和varying[16],是箭頭化的,用Haskell實現的拉取式FRP。
- Flapjax,是一個用JavaScript實現的行為/事件式FRP。
- React[17],是用於函數式響應編程的一個OCaml模塊。
- Sodium[18],是獨立於UI框架的推送式FRP實現,支持多種程式語言比如Java、TypeScript和C#。
- ReactiveX,由它的JavaScript實現rxjs[19]而流行,是實現函數式響應式編程的綜合性跨平台范型,將數據當作可觀測者的流來處理。
- Dunai[20],是支持類和箭頭化FPR的,使用單子流函數的一個Haskell的快速實現。
另請參閱
參考來源
外部連結
Wikiwand - on
Seamless Wikipedia browsing. On steroids.