版權(quán)歸原作者所有,如有侵權(quán),請(qǐng)聯(lián)系我們

[科普中國]-函數(shù)式反應(yīng)式編程

科學(xué)百科
原創(chuàng)
科學(xué)百科為用戶提供權(quán)威科普內(nèi)容,打造知識(shí)科普陣地
收藏

函數(shù)式編程(functional programming)或稱函數(shù)程序設(shè)計(jì),又稱泛函編程,是一種編程典范,它將計(jì)算機(jī)運(yùn)算視為數(shù)學(xué)上的函數(shù)計(jì)算,并且避免使用程序狀態(tài)以及易變對(duì)象。函數(shù)式反應(yīng)式編程(FRP) 是一種采用函數(shù)式編程的基礎(chǔ)部件(如 map、reduce、filter 等)進(jìn)行反應(yīng)式編程(異步數(shù)據(jù)流編程)的編程范式。FRP 被用于GUI、機(jī)器人和音樂方面的編程,旨在通過明確時(shí)間模型來簡(jiǎn)化這些問題。

簡(jiǎn)介函數(shù)式編程是一種編程范式, 它將計(jì)算機(jī)運(yùn)算視為數(shù)學(xué)上的函數(shù)計(jì)算。 函數(shù)式編程的基礎(chǔ)是 lambda 演算??墒牵瑔渭円揽窟@兩句解釋是不足以分辨函數(shù)式編程跟其他編程范式的不同之處, 這需要結(jié)合函數(shù)式編程語言的特性去分析。 首先, 函數(shù)式編程中的函數(shù)指的是“高階函數(shù)”, 即函數(shù)可以作為參數(shù)傳入到其他函數(shù), 函數(shù)也可以返回函數(shù)作為結(jié)果1。

函數(shù)式反應(yīng)式編程自1997 年 FRP 被提出以來,它產(chǎn)生了多種形式。其中一條多樣性的坐標(biāo)軸是語義從離散到連續(xù)。 另一條軸是 FRP 系統(tǒng)可以如何被動(dòng)態(tài)地更改。

如事件驅(qū)動(dòng) FRP 和 Elm 0.17 版本之前的形式那樣,它們要求更新過程是離散的,且由事件驅(qū)動(dòng)。這些形式在 FRP 的實(shí)踐中被加以推崇,它們專注于擁有簡(jiǎn)單 API 的語義,可以在如機(jī)器人學(xué)或 Web 瀏覽器中被高效地實(shí)現(xiàn)。在這些形式下,行為和事件的概念通常會(huì)被組合成總是擁有當(dāng)前值的信號(hào),但是它會(huì)被離散地改變。FRP 的最早形式采用了連續(xù)的語義,旨在抽象出對(duì)程序的意義無關(guān)緊要的操作細(xì)節(jié)。這種形式的關(guān)鍵屬性為:

建模值在連續(xù)時(shí)間內(nèi)變化,叫做“行為”,后稱為“信號(hào)”。

建?!笆录卑l(fā)生在離散的時(shí)間點(diǎn)上。

系統(tǒng)可在相應(yīng)事件時(shí)被改變,通用術(shù)語為“切換”。

從反應(yīng)模型中分離出求值細(xì)節(jié),如采樣率。

這種 FRP 的語義模型在無副作用的語言中通常是隨時(shí)間變化的連續(xù)函數(shù)。

交互式 FRP需要被指出的是,普通的 FRP 模型,從輸入到輸出都不太適合交互式程序。在從輸入映射到輸出的過程中缺乏“運(yùn)行”程序的能力可能意味著必須使用以下解決方案之一:

創(chuàng)建一個(gè)用于輸出的數(shù)據(jù)結(jié)構(gòu)來表示活動(dòng)。活動(dòng)必須被一個(gè)外部的解釋器或環(huán)境來運(yùn)行。它繼承了最初 Haskell 的流式 I/O 的全部難點(diǎn)。

使用箭頭化的 FRP 并嵌入能夠處理動(dòng)作的箭頭。活動(dòng)也必須要有ID,以便讓它們分別維護(hù)不可變存儲(chǔ)之類的東西。這就是 Fudgets 庫采取的辦法。

最新穎的方法就是允許活動(dòng)運(yùn)行在(在 IO 單子中)但將它們結(jié)果的接收推遲到之后。它采用了事件 Event 和 IO 單子之間的交互,并與更加面向表達(dá)式的 FRP 兼容:

planNow :: Event (IO a) -> IO (Event a)實(shí)現(xiàn)存在兩種類型的 FRP 系統(tǒng),基于推送的和基于拉取的。基于推送的系統(tǒng)接收事件并將它們推過一個(gè)信號(hào)網(wǎng)絡(luò)來達(dá)到某種結(jié)果?;诶〉南到y(tǒng)會(huì)等待結(jié)果請(qǐng)求,并逆向通過該網(wǎng)絡(luò)來取回請(qǐng)求的結(jié)果。

某些 FRP 系統(tǒng)例如 Yampa 使用采樣。在一個(gè)定期內(nèi),采樣被推過一個(gè)信號(hào)網(wǎng)絡(luò)。這種方法有兩個(gè)缺點(diǎn):在一個(gè)定期內(nèi)處理樣本的計(jì)算量會(huì)非常大,而且網(wǎng)絡(luò)必須在等待采樣區(qū)間的間隔時(shí)找出輸入的更改。采樣就是個(gè)基于推送的 FRP 示例。

Hackage 上的 Reactive 和 Etage 庫介紹了一種叫做“推送-拉取 FRP” 的方式,它將基于推送和基于拉取的 FRP 最好的部分結(jié)合在了一起。按照這種方式,只有在定義純粹的流上(如一系列時(shí)間固定的事件)的下一個(gè)事件被請(qǐng)求時(shí),才會(huì)構(gòu)造該事件。這些定義純粹的流的行為類似于 Haskell 中的惰性列表。此為基于拉取的部分?;谕扑偷牟糠謺?huì)在系統(tǒng)外部的事件被帶入系統(tǒng)中時(shí)才會(huì)使用。外部的事件會(huì)被推送給消費(fèi)者,這樣它們就可以在它發(fā)布的瞬間找到該事件。

reflex 是一個(gè)高效的,用 Haskell 實(shí)現(xiàn)的推送/拉取式 FRP,主要用于瀏覽器/DOM、SDL 和 Gloss

reactive-banana 是一個(gè)用 Haskell 實(shí)現(xiàn)的目標(biāo)不可知的推送式 FRP

netwire 和 varying 是被箭頭化的,用 Haskell 實(shí)現(xiàn)的拉取式 FRP

Flapjax 是一個(gè)用 JavaScript 實(shí)現(xiàn)的行為/事件式 FRP

本詞條內(nèi)容貢獻(xiàn)者為:

王沛 - 副教授、副研究員 - 中國科學(xué)院工程熱物理研究所