[英]Shared resource in functional programming
我正在使用Clojure編寫應用程序,並遵循函數式編程范例。 在這個應用程序中,我有兩個HTTP端點: /rank
和/invite
。 在/rank
該應用會根據其得分對客戶的列表進行排名。 在/invite
該應用程序收到了一個客戶的邀請,從而邀請了另一個客戶,這將使某些客戶的分數發生變化。
來自客戶的數據保存在一個稱為record
的地圖矢量中。
暫且不提參考透明性, record
應該是端點之間的共享資源,一個應讀取它並在排名函數中使用它來響應HTTP請求,另一個應讀取它並更新其中的分數。
現在,考慮到函數式編程,無法更新record
,因此/invite
端點應該讀取它並返回新record'
,問題是, /rank
端點已設置為使用record
,但是當生成新record'
時,它應該用它代替原來的。
我了解在這種情況下,整個應用程序在功能上不能完全純凈。 它從文件讀取初始輸入,並從外部環境接收請求,所有這些都使處理這些部分的功能不透明。 幾乎每個程序都將包含這些一小部分的非功能代碼,但是為了嘗試不向該應用程序添加更多這些非功能性代碼,並且該應用程序僅是為了執行一些功能性編程,所以我並沒有堅持record
到數據庫之類的東西,因為如果是這種情況,則可以解決問題,因為我可以調用一個函數來更新數據庫上的記錄。
到目前為止,我最好的主意是:端點是使用Compojure的routes
函數創建的,因此在/invite
端點中,我應處理新record'
vector,並重新創建/rank
端點,並為其提供record'
。 我正在努力重建/rank
這一部分,我試圖再次調用routes
並再次定義所有端點,以希望它將覆蓋routes
的原始調用,但是正如所料,我相信Compojure遵循函數式編程,一旦創建,路由是不可變的,並且路由的新調用不會覆蓋任何內容,它只會創建將留在空白中的新路由,而不附加到HTTP請求。
那么,有可能用純功能代碼做我想做的事嗎? 還是不可避免地要破壞參照透明性,我應該將record
持久保存到文件或數據庫中以進行更新嗎?
PS:我不知道這是否相關,但是我對Clojure和任何類型的Web交互都是陌生的。
Clojure(與Haskell相反)不是純凈的,它具有自己的結構來管理對共享狀態的更改。 它不會使用類型系統(如Haskell中的IO monad)隔離不純,而是促進使用純函數並使用不同類型的引用( atom
, agent
, ref
)管理狀態,這些引用定義了明確的語義以及如何以及何時更改狀態。
對於您的方案,Clojure的atom
將是最簡單的解決方案。 它就如何管理其狀態提供了明確的合同。
我將創建一個在原子內保存您的記錄的變量:
(def record (atom [])) ;; initial record is empty
然后,在rank
終結點中,可以使用deref
或將@
用作語法糖來使用它的值:
(GET "/rank" []
(calculate-rank @record))
而您的invite
端點可以使用swap!
原子地更新您的記錄值swap!
:
(POST "/invite/:id" [id]
(invite id)
(swap! record calculate-new-rank id)
(create-response))
您的calculate-new-rank
函數如下所示:
(defn calculate-new-rank [current-record id]
;; do some calculations
;; create a new record value and return it
(let [new-record ...]
new-record))
將使用存儲在atom中的數據的當前版本以及其他可選參數來調用您的函數,並且函數的結果將作為您的atom的新值安裝。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.