函數式編程終於成爲了主流!
摘要:面向對象編程和命令式編程仍然是現代軟件開發的主要範式,而函數式編程語言在生產代碼庫中相對較少。隨着編程語言擴展對函數式編程方法的支持,以及軟件開發新框架的迭代,函數式編程正迅速通過各種不同的途徑進入越來越多的代碼庫。
原文鏈接:https://github.com/readme/featured/functional-programming
聲明:本文爲 CSDN 翻譯,轉載請註明來源。
作者 | KLINT FINLEY
譯者 | 彎月 責編 | 屠敏
出品 | CSDN(ID:CSDNnews)
Meddbase 是一家於 2005 年創立的醫療保健軟件公司,Paul Louth 在這家公司內創建了一支出色的開發團隊。但隨着公司的發展,他們的代碼 Bug 數量也在增加。從某種程度上說,這是必然的結果。功能越多,代碼越多;代碼越多,Bug 就越多。但缺陷率的增長速度超出了 Louth 的預期。
他表示:“我們看到越來越多相同類型的錯誤。很明顯某個地方出了問題,但我們不清楚究竟是什麼。我的直覺告訴我這個問題是由複雜性引起的。”
函數式編程
他們使用的編程語言是 C#,該語言剛剛添加了對語言集成查詢(LINQ)的支持。LINQ 是一種查詢數據源的語言,不僅可以查詢數據庫,而且還可以查詢代碼庫中的對象。LINQ 將函數式編程範式引入了 C#,瞭解 LINQ 可以幫助你瞭解更多關於函數式編程的知識。
函數是可在程序中重複完成特定任務的一段代碼,可以避免重複編寫相同功能的代碼,是一種編程標準。例如,你可以編寫一個根據圓的直徑計算周長的函數,或者編寫一個根據某人的出生日期計算星座的函數。
但是,正如 Cassidy Williams(Netlify 遠程開發者體驗與教育主管)在 ReadME 項目的函數式編程指南中寫道,函數式編程不僅僅是編寫函數,這是一種強調使用 “純函數” 編寫程序的範例,即無狀態的函數,在輸入相同的情況下總能返回相同的結果,而且在返回結果時不會產生“副作用”。換句話說,**純函數不會更改任何現有數據或修改應用程序的其他邏輯。**例如,如果計算圓周的函數修改了一個全局變量,那麼它就不是純函數了。
在函數式編程中,數據通常被視爲是不可變的。例如,用函數式編寫的程序不會修改數組的內容,而是會生成一個修改後的數組副本。與數據結構和邏輯交織在一起的面向對象編程不同,函數式編程強調數據和邏輯的分離。
Remote 的開發者體驗負責人 Williams 寫道:“在考慮結構良好的軟件時,你可能會想到易於編寫、易於調試並且包含很多可重複使用功能的軟件,而這說的就是函數式編程!”
對於需要負責不斷增長的大型代碼庫的團隊來說,函數式編程是一個福音。由於編寫的代碼具有很少副作用,且數據結構不會變化,因此負責代碼庫某個部分的程序員不太可能破壞另一個程序員正在開發的功能。此外,追蹤錯誤也更加容易,因爲代碼中可以發生變化的地方很少。
隨着 Louth 對函數式編程的深入研究,他意識到這種方法可以解決大型面向對象代碼庫所面臨的一些問題,就好像他們團隊遇到的問題。然而,遇到此類問題的人不止他一個。在過去十年中,隨着越來越多的開發人員和組織尋找方法來控制軟件的複雜度,渴求構建更安全、更強大的軟件,人們對函數式編程的興趣呈爆炸式增長。
Williams 表示:“在真正瞭解函數式編程後,我就不打算再使用其他範式了。” 實際上,他在大學期間使用 LISP 的衍生編程語言 Scheme 和 Racket 時就愛上了函數式編程。“面向對象編程很不錯,至今仍有很多人使用。但很多開發人員在接觸函數式編程後,就會成爲忠實的粉絲。”
當然,面向對象編程和命令式編程仍然是現代軟件開發的主要範式,而 Haskell 和 Elm 之類的 “純” 函數式編程語言在生產代碼庫中相對較少。但隨着編程語言擴展對函數式編程方法的支持,以及軟件開發新框架的迭代,函數式編程正迅速通過各種不同的途徑進入越來越多的代碼庫。
函數式編程在主流語言中的興起
函數式編程的起源可以追溯到 20 世紀 50 年代後期 LISP 編程語言的創建。儘管幾十年來 LISP 及其衍生語言(如 Common LISP 和 Scheme)一直有一批忠實的追隨者,但到了 70~80 年代,隨着面向對象編程的興起,函數式編程黯然失色。
到了 2010 年,隨着代碼庫規模的日漸壯大,很多團隊都面臨着與 Louth 開發團隊相同的問題,於是人們對函數式編程的興趣再次高漲。
Scala 和 Clojure 將函數式編程引入到了 Java 虛擬機,而 F# 將這種範式引入到了. NET。推特將他們的大部分代碼遷移到了 Scala,而 Facebook 則選用了 Haskell 和 Erlang 等古老的函數式語言來解決特定問題。
與此同時,人們對在 JavaScript、Ruby 和 Python 等面嚮對象語言中應用函數式編程技術的興趣也在增長。《Elm in Action》一書的作者 Richard Feldman 表示:“所有這些語言都開始支持方便實現函數式風格的功能,幾乎每一種語言支持的範式都在增加。”
2014 年,蘋果公司推出了 Swift,這是一種新的多範式語言,包括對函數式編程的強大支持。該語言的發佈證明,函數式編程支持已成爲新編程語言的必備特性,就像對面向對象編程的支持一樣。Feldman 在 2019 年的一次題爲 “爲什麼函數式編程不是主流?” 的演講中說,函數式編程馬上就要成爲主流了。或者至少是主流的一部分。他說:“有些事情發生了變化,在 90 年代人們不認爲這種風格是積極的,而如今它是積極的想法已經成爲一種主流。”
對函數式編程的支持級別和性質因語言而異,但有一項特定功能已成爲標準。Remote 後端工程師 Tobias Pfeiffer 表示:“可以說,有一項功能已經勝出,那就是高階函數,即接受或返回另一個函數的函數。你可以通過這種方式創建函數塊來優雅地解決問題。”
由於對高階函數的支持,如今開發人員可以將函數式編程引入到自己的代碼庫,而無需重新構建現有應用程序。當 Louth 團隊決定轉而使用函數式編程模型時,他們決定使用 F# 編寫應用程序中的新代碼,因爲它提供了強大的函數式支持,而且還可以與. NET 平臺保持兼容。對於已有的 C# 代碼,他們決定使用支持函數式編程的語言,同時還會使用 Language-ext(這是 Louth 自己編寫的函數式 C# 開源框架)。
Louth 表示:“我們不想推倒一切重寫,因此在現有代碼的基礎之上添加新功能時,我們會按照函數式編程範式來編寫。”
對於某些人來說,使用 Java、JavaScript 或 C# 等面向對象的語言進行函數式編程感覺就像逆流而上。Arista Networks 的工程經理 Gabriella Gonzalez 說:“語言可以引導你採用某些解決方案或風格。在 Haskell 中,阻力最小的方法是函數式編程。你可以在 Java 中進行函數式編程,但肯定不是最順利的方式。”
對於一些混合範式來說,一個更大的問題是,**如果代碼中包含其他編程風格,那你就無法獲得純函數的保證。**Williams 表示:“如果你正在編寫的代碼可能有副作用,那麼就不再是函數式了。你或許可以依賴部分代碼庫。我編寫了各種非常模塊化的函數,因此沒有任何代碼會影響到它們。”
使用嚴格的函數式編程語言可以降低在代碼中意外引入副作用的可能性。Louth 表示:“使用 C# 之類的語言編寫函數式代碼的關鍵是你必須小心,因爲你可以走捷徑,一旦你的代碼不是純函數式編,那麼就會導致混亂。Haskell 會強制你編寫函數式的代碼,可以保證你的代碼不出亂子。” 這就是 Louth 團隊在一些新項目中使用 Haskell 的原因。
但純粹與否往往是主觀感受。Pfeiffer 說:“LISP 是最早的函數式編程語言,但它具有可變的數據結構。”即使你的語言是純函數式,而且使用不可變的數據結構,但在接受用戶輸入的那一刻就有可能引入 “雜質”。雖然用戶輸入可能包含“不純” 的元素,但大多數程序都離不開用戶輸入,所以純粹是有限度的。Haskell 管理用戶輸入和其他不可避免的 “雜質” 的方法是給代碼貼上明確的標籤,並保證隔離,但不同的語言所劃定的界限有所不同。
拓展函數式思維
儘管現如今大多數主要編程語言都可以使用函數式編程,但開發人員不一定會利用這些特性。函數式編程的思維方式與命令式或面向對象編程截然不同。Williams 表示:“讓我大喫一驚的是,你不能在函數式編程中採用傳統的數據結構。我不必爲一棵樹建立一個對象,直接使用樹就可以。感覺應該是不行,但確實可行。”
JavaScript 庫 React 是如今開發人員接觸這種新思維方式的主要方式。由於 React 接受很多可變性,因此最好是把 React 視爲 “近似於函數式”,但生態系統確實支持這種方式,而且還鼓勵人們將函數式編程作爲常見問題的解決方案,但以前人們肯定會尋求其他方式。
ClojureScript 的維護者 David Nolen 在他的 ReadME 項目故事中寫道:“儘管 React 表面看起來是面向對象,但它率先爲 UI 編程提供了函數式方法。”
例如,最近推出的 React Hooks 是一組幫助開發人員管理狀態和副作用的函數,而 Redux 則大量採用了函數式方法。
Williams 表示:“雖然 React 不是純函數式,但它鼓勵人們以函數式編程的方式思考。例如,它爲前端開發人員引入了 map 和 reduce 循環。自此,前端開發人員無需再編寫任何 for 循環。”
另一方面,Gonzalez 則認爲函數式編程通過另一種途徑躋身主流之列:**特定領域的語言。真正擅長解決某個問題的語言會更加容易被採納。**例如,Nix 包管理系統的表達式語言。“它是甚至比 Haskell 更純粹的函數式語言,因爲使用這種語言編寫的代碼更加難以附帶有副作用或可變性。”Nix 的用途很單一,至少不適合構建 Web 服務器。它的目的只有一個:構建包。但由於它是爲這種特定任務而構建的,所以需要構建工具的開發人員都會使用它,即使他們不會嘗試使用函數式編程語言。“我認爲,隨着時間的推移,通用語言會越來越少,而專用語言越來越多,其中許多都是函數式語言。”
函數式編程的未來
與軟件開發領域的許多其他方面一樣,函數式編程的未來在很大程度上取決於圍繞函數式語言和概念構建的開源社區。例如,採用純函數式編程的一個障礙是,面向對象編程或命令式編程的庫在數量上擁有絕對優勢。從 Python 的數學與科學計算包到 Node.js 的 Web 框架,很多解決常見問題的代碼庫都不是以函數式風格編寫的,但我們無法因此而棄用它們。Louth 團隊中使用 Haskell 時就遇到了這樣的問題:“Haskell 有一個非常成熟的生態系統,但我們的困擾在於無法使用一些非專業的庫。”
爲了克服這個障礙,函數式編程社區必須團結起來,創建新的庫,幫助開發人員選擇函數式編程。
從 language-ext 庫到 Redux,再到 Nix,許多人已經在努力中了。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/8Sp6Hv4FCxFkDp-CY2oZbw