函數式編程有什麼弊端?

從技術上來講,函數式編程有哪些弊端?


先佔個坑。。

接觸過Haskell、Clojure等語言,也做過PL相關的「研究」,簡單說說體會。。

1. 函數式的代價——複雜度

分析Haskell程序的時間、空間複雜度比較麻煩。首先寫Haskell程序往往大量」重用「內建/自定義數據結構,為了實現數據的持久性(或「不變性」)會增加空間和時間消耗(實現策略有很多,但治標不治本),好處是控制副作用。然後函數組合、高階函數的開銷也不小,也會產生額外的中間數據,優點是抽象能力強、利於模塊化。 最後,惰性求值使程序求值模型複雜了,濫用也會導致效率下降。針對函數式語言相關的profiling工具也比較缺。

關於何時何處使用惰性求值學術界一直在研究,POPL14有篇論文Profiling for Laziness就談了這個(裝個X,我和作者郵件討論過。。)。 前面兩點涉及函數式語言的實現問題,我也一直在關注一項技術,supercompilation,這個已經用在了GHC里。簡單來說,就是消除中間數據結構+做程序特化減小函數代價。

寫高效的程序需要經驗、對底層實現(標準庫+語言核心)的一點理解,C++不也這樣么,不過C++對程序的控制粒度細一點。

2. 模塊化和並行化

模塊,應該是基本軟體元素(操作、類型、變數等)的結合體,為復用性、可擴展性服務

Huges在Why Functional Programming Matters(函數式編程經典軟文)說「函數式編程的主要價值就是大大改進了程序的模塊化程度。」 高階函數、遞歸數據類型、惰性求值是」新的粘合劑」。

但是這些主要提供細粒度的模塊化,程序員用得飛起,需要自己「設計」的也多了。Huges指出「採用函數式編程的程序員必須努力確保程序使用更小、更簡單、更通用的模塊」,這是個挑戰。

粗粒度的,函數式語言也有datatype, class, type class等概念,避免了OO的class將類型-模塊糅合產生的問題(不展開細說了),當然良好的使用、設計同樣需要很多經驗(函數式也有「設計模式」的好伐,社區的人高冷地不願意寫?)

另外「無狀態編程」對模塊間的交互也是個問題。

綜合以上,可以用Vczh的話總結"最難的地方就是使用正確的工具來對問題進行正確的建模,這比設計模式要難多了。設計模式只需要封裝變化就好了,函數式你不僅要封裝變化,還得把(不是業務上,而是邏輯上)能重用的東西都拿出來重用,才能讓整個程序的味道變好。"

下面談並行化。

「純函數式「的問題:比如很多演算法需要數組元素」就地更新「,「純函數式」不利於對事物狀態變化的模擬。不能為追求無副作用、無鎖而無視現實。

惰性求值的問題:對這個為什麼不利於並行化我還理解不夠。。

3. 類型系統、程序驗證等


作為編程範式的一種或者說最基本的一種(可以說所有語言都具備),函數式編程無所謂弊端一說。

當然問題所指應該是那些自詡具備某些獨一特徵,並以摒棄其他語言中的糟粕而得意的一類語言所宣稱的某種狹義(隘)範式。

比如所謂無狀態/副作用,大家現在都明白真正無狀態是不可能的,函數式的方式就是把狀態和邏輯關聯在一起,有時這是可行甚至必須的,但還有很多時候其實是附帶了多餘的邏輯負擔。至於因此得到無(隔離)副作用的好處,很多時候其實無關緊要,即便真有這種需求,也可以不依賴前者。

至於很多人提到的性能一說,則其實並無關係,摒棄一些底層實現本身技術上的缺陷,語言層面上完全可以做到一致的優化。

所謂殊途同歸,如果各種語言相互之間並蓄兼收,可望在不久之後便會不再在這種基本細節上糾纏不休。實際上自從函數對象概念在c++這種命令式語言逐漸普及開來越來越多的庫代碼都有意無意的釆納了更純粹的函數式風格的介面形式,雖然這種風氣的好壞還不太清楚。


學的人容易感到智商不夠用


沒接觸過別的,只用過Haskell.

函數式編程的主體思想是用遞歸解決問題,所以在debug的時候要比其他語言難得多。

還有就是在解決問題之前,需要做非常好的邏輯架構,第一步做什麼,第二步做什麼一直到得出解必須都要想到。要不然中途想加個變數什麼的,那代碼就使勁改吧,涉及到得方程全部都要改。Java里你一個全局變數可以解決的問題,Haskell里需要從頭傳到尾。

函數式編程可以優雅的解決遞歸問題,但是其它問題。。。。。說實話我不敢恭維。有的時候你用C解決一個簡單的問題用函數式編程需要繞很多個圈。


難學是唯一的技術上的缺點。用函數式編程最難的地方就是使用正確的工具來對問題進行正確的建模,這比設計模式要難多了。設計模式只需要封裝變化就好了,函數式你不僅要封裝變化,還得把(不是業務上,而是邏輯上)能重用的東西都拿出來重用,才能讓整個程序的味道變好。

不明白的話可以看看Haskell為什麼Traversable要繼承自Foldable。


我感覺,很抽象,你必須對全局都充分的了解透徹,才能寫好。而且,純函數式編程無副作用,這在某種程度上和我們的認識觀不符合。


學好函數式編程是要數學基礎的,尤其是了解數學是如何從更高層面抽象已經被抽象過的東西。


我覺得在於早期的知識積累。就如同從小用著windows,給一個linux就會問c盤去哪裡了?

fp有自己的數據結構,有自己的設計模式。這些不同於從c開始學習的路線,甚至大部分知識都沒有辦法遷移借鑒。所以會覺得難吧。

我們現在使用的計算機是馮諾依曼體系下對圖靈機的實現。上層的語言等知識背景都是基於這種體系結構的。

當然世界上還有其他的計算模型lambda演算,基於這種模型來思考計算會於圖靈模型完全不同。

如果開始學編程的時候在一個lisp機上寫lisp,估計也會詫異c語言沒有垃圾收集吧。


肯定是不好學,跟命令式的抽象程度不在一個層次。有的代碼根本看不懂,不過有數學性質保證即使你看不懂也不那麼容易用錯。:)


最近開始認真接觸函數式編程,講點兒感受吧:

函數式編程的確可以把一些演算法的實現濃縮成很少行數,這是優勢;可是這濃縮的方法五花百門,也因各人的思維差別很大,編寫的方法也比其他範式變化更多,編程時很難選擇合適的方式,也使代碼的可讀度降低。

另外,函數式編程的邏輯跟機器有差,運行相對較慢。但我相信如果用合適的optimization是可以很容易解決的。

不過函數式編程的優點也很明顯,有很多演算法的邏輯會跟突出準確,在並行計算也容易實現得多。


簡單來講,你的思維要足夠強大到能模擬出所有底層細節,保證所有的細節都在你的設計之內。不然就是一堆bug,程序規模越大缺陷越多。


信息量少。


面向對象的優勢就是函數式編程的弊端……


提個不太重要的問題:

很多函數式語言的支持者吹lambda演算。實際上lambda演算實現遞歸必須藉助quine,quine必須把一些代碼複製兩次(雖然不需要重複寫很多次,但是在邏輯上也是複製了兩次),複製代碼顯然是不好的編程習慣,就不多解釋其危害了。

雖然大部分函數式語言不需要手寫這種間接實現遞歸的東西,但是很多函數式語言的支持者繼承了這種思維。


推薦閱讀:

如何看待QQ6.5更新以後MSE報毒?
對於一個以後想做信息安全方面的學生,是否有必要進acm校隊?
python3中的urlopen對於中文url是如何處理的?
如何用 C 語言在 Windows 編寫一個遊戲?
C 語言進階有哪些優秀的代碼可以閱讀?

TAG:編程語言 | 編程 | 函數式編程 |