簡介
函式式編程是一種編程範式, 它將計算機運算視為數學上的函式計算。 函式式編程的基礎是 lambda 演算。可是,單純依靠這兩句解釋是不足以分辨函式式編程跟其他編程範式的不同之處, 這需要結合函式式程式語言的特性去分析。 首先, 函式式編程中的函式指的是“高階函式”, 即函式可以作為參數傳入到其他函式, 函式也可以返回函式作為結果。
函式式反應式編程自1997 年 FRP 被提出以來,它產生了多種形式。其中一條多樣性的坐標軸是語義從離散到連續。 另一條軸是 FRP 系統可以如何被動態地更改。
如事件驅動 FRP 和 Elm 0.17 版本之前的形式那樣,它們要求更新過程是離散的,且由事件驅動。這些形式在 FRP 的實踐中被加以推崇,它們專注於擁有簡單 API 的語義,可以在如機器人學或 Web 瀏覽器中被高效地實現。在這些形式下,行為和事件的概念通常會被組合成總是擁有當前值的信號,但是它會被離散地改變。FRP 的最早形式採用了連續的語義,旨在抽象出對程式的意義無關緊要的操作細節。這種形式的關鍵屬性為:
•建模值在連續時間內變化,叫做“行為”,後稱為“信號”。
•建模“事件”發生在離散的時間點上。
•系統可在相應事件時被改變,通用術語為“切換”。
•從反應模型中分離出求值細節,如採樣率。
•這種 FRP 的語義模型在無副作用的語言中通常是隨時間變化的連續函式。
互動式 FRP
需要被指出的是,普通的 FRP 模型,從輸入到輸出都不太適合互動式程式。在從輸入映射到輸出的過程中缺乏“運行”程式的能力可能意味著必須使用以下解決方案之一:
創建一個用於輸出的數據結構來表示活動。活動必須被一個外部的解釋器或環境來運行。它繼承了最初 Haskell 的流式 I/O 的全部難點。
使用箭頭化的 FRP 並嵌入能夠處理動作的箭頭。活動也必須要有ID,以便讓它們分別維護不可變存儲之類的東西。這就是 Fudgets 庫採取的辦法。
最新穎的方法就是允許活動運行在(在 IO 單子中)但將它們結果的接收推遲到之後。它採用了事件 Event 和 IO 單子之間的互動,並與更加面向表達式的 FRP 兼容:
實現
存在兩種類型的 FRP 系統,基於推送的和基於拉取的。基於推送的系統接收事件並將它們推過一個信號網路來達到某種結果。基於拉取的系統會等待結果請求,並逆向通過該網路來取回請求的結果。
某些 FRP 系統例如 Yampa 使用採樣。在一個定期內,採樣被推過一個信號網路。這種方法有兩個缺點:在一個定期內處理樣本的計算量會非常大,而且網路必須在等待採樣區間的間隔時找出輸入的更改。採樣就是個基於推送的 FRP 示例。
Hackage 上的 Reactive 和 Etage 庫介紹了一種叫做“推送-拉取 FRP” 的方式,它將基於推送和基於拉取的 FRP 最好的部分結合在了一起。按照這種方式,只有在定義純粹的流上(如一系列時間固定的事件)的下一個事件被請求時,才會構造該事件。這些定義純粹的流的行為類似於 Haskell 中的惰性列表。此為基於拉取的部分。基於推送的部分會在系統外部的事件被帶入系統中時才會使用。外部的事件會被推送給消費者,這樣它們就可以在它發布的瞬間找到該事件。
•reflex 是一個高效的,用 Haskell 實現的推送/拉取式 FRP,主要用於瀏覽器/DOM、SDL 和 Gloss
•reactive-banana 是一個用 Haskell 實現的目標不可知的推送式 FRP
•netwire 和 varying 是被箭頭化的,用 Haskell 實現的拉取式 FRP
•Flapjax 是一個用 JavaScript 實現的行為/事件式 FRP