副作用是程式設計頭號敵人!如何剝離它?- spin

banq發表於2022-03-01

隨著時間的推移,我注意到一種設計啟發式方法,它極大地幫助了我完成無數專案。
這種啟發式的地方在於它在概念上易於理解和應用,但它自然會引導您更接近函數語言程式設計
事實上,這與 Haskell 處理 IO 的方式非常相似。它也是 React 等現代 Web/UI 框架的核心理念。
 
為了說明這一點,讓我們看一個例子:
你正在處理一個涉及安排未來使用者通知的事情:有一些輸入需要考慮,比如使用者喜歡的時間和時間的推移;你已經決定提前一週安排它們;你需要定期安排新的通知,但你也可能需要刪除或重新安排現有的通知。
 

對於未來一週的每一天foreach
  是否沒有安排通知?if
    現在就生成並安排(副作用!)。
  否則,應該重新安排嗎?else
    刪除現有的通知(副作用!)。
    生成並安排新的通知(副作用!)。

 
在這個ifelse例子中,我們在進行處理的同時也在修改世界。
副作用本質上並不壞:事實上,它們是編寫軟體的全部意義所在。如果你的程式碼沒有效果,就不會有人使用它。

不過,還是要問問自己,你怎麼能把這些副作用儲存到最後?
歸根結底,辦法就是建立一些資料,描述需要採取的任何行動。

對於未來一週的每一天foreach
  是否沒有安排通知?if
    在輸出中新增一些資料,描述一個新的通知
  否則,是否應該重新安排?else
    在輸出中新增一些資料描述如何刪除該通知
    並新增更多關於如何安排其替換的資料



這段虛擬碼輸出:

[{action: "remove", notificationId: 4},
 {action: "create", time: "some ISO8601 time"}]


然後你會把這些資料交給另一個更簡單的函式,由這個更簡單的函式來執行這些副作用。
 
誠然,這種啟發式方法將導致你做一些額外的工作,但也有很多好處。其中。

首先,它創造了一個自然邊界,對測試非常有用。上面的虛擬碼中的邏輯(如 "否則,是否應該重新安排?")實際上可能相當複雜。

在系統中,決定實施什麼變化的邏輯往往比實施上述變化的邏輯要複雜得多。透過劃定這個邊界,你可以獲得編寫更少的艱鉅的測試的好處。要模擬的東西更少,而且很容易為資料寫斷言。

第二,除錯變得更加容易。能夠問一個系統要做什麼,並在它開始執行之前收到其計劃的完整快照,這真的很神奇。這對於更復雜的功能來說尤其如此,例如與外部系統的同步。

最後,這種方法使你能夠輕鬆地進行分層轉換。使用上面的例子,假設你的通知排程器應該避免某些假期。你可以寫一個簡單的函式,接收一個排程行為的列表,並過濾掉任何發生在特定日期的行為。


 

相關文章