純函數語言程式設計的缺點

banq發表於2016-06-08
本文總結了函數語言程式設計的幾大缺點,其中主要焦點是可變性狀態Mutation是否應該是預設,union-find演算法的Dr. Harrop說:目前我們還沒有發現一個有效率的純函式的union-find集合。也就是說:對於有狀態的操作命令式操作會比宣告式操作更有效率。

純函式程式設計的缺點有:

1.沒有純粹的函式式的非排序的字典或集合Set

自從上世紀90年代字典在軟體中應用以來已經到達高峰,字典是一個每個程式設計師都能在標準庫中常用的集合。

純函式或持久資料結構,比如那些在Okasaki’s fabulous monograph被發現的事物都能成為偉大的工具,他們提供了在不用擔心可變狀態Mutation的情況下,能重用舊集合版本實現的持久化功能,在大多數情況下(特別是邏輯程式設計和編譯程式)。它們會使得解決方案更簡潔和清晰。 部分原因是它使得回溯變得平常,然而,持久成為效能方面的一個很大成本,也就是持久化效能很差: 純函式字典通常比一個正常的雜湊錶慢10倍以上,也曾經發現慢過40倍。

此外,大多數函數語言程式設計語言(OCaml,Haskell、Scala)都不能表達一個快速的通用可變的雜湊表,因為他們缺乏殺手鐧:具體化的泛型、值型別和快速GC寫屏障(write barrier)。

當心,有人試圖聲稱,Haskell的純函式字典比Haskell的可變的雜湊表更快。正確的結論是Haskell的可變雜湊表相比其他語言的實現是緩慢的,所以,顯得純函式字典比較快。

2. 沒有純函式式弱雜湊表
使用垃圾回收機制收集命令式語言,一個圖的頂點與邊之間的關係可以使用弱雜湊表表達,垃圾回收機制會替你收集其子圖。而因為在純函式程式設計中,沒有純函式弱雜湊表,所以,你必須自己編寫垃圾回收機制。

請注意,大多數開發人員從來沒有用過弱雜湊表,因此讓他們編寫自己的垃圾回收機制是多大的一個問題。

3. 沒有純函式的併發集合
根據定義,不可變集合不能支援併發可變狀態操作,因此,如果你想共享一個可變狀態的集合,比如記憶體資料庫等,在這點上,沒有有效的純函式式解決方案可替換。

4.大多數圖演算法看起來很差,當以FP風格編寫時更慢。
純函數語言程式設計是解決某些問題的偉大工具,但是對於圖演算法這點,純函式解決方案在速度和清晰都方面都很差。

比較Prim’s algorithm in 12 lines of PythonPrim’s algorithm in 20 lines of Haskell. 為什麼Haskell使用Prim的演算法? 可能因為Kruskal的演算法是基於union-find集合建立,而目前還沒有發現有效率的純函式的union-find 集合.

5. 傳統命令式資料結構和演算法的慣性是巨大的
除了圖演算法,電腦科學65年發表的文獻幾乎完全集中在命令式的解決方案,因此命令式程式設計師會很容易站在巨人肩膀上,而純函式程式設計師只能從零開始,記得幾年前,Haskell還是博士論文題目。

幾個haskell程式設計師編寫了通用的並行快速排序Haskell程式,點選這裡看看發生了什麼

6. 所有函數語言程式設計的實現,包括純與不純的,都會產生太多的分配設計。
1960左右,麥卡錫發明了Lisp。核心資料是結構連結串列。每一個列表節點是一個獨立的堆分配的塊。所有現代的函式性語言都是由此演變而來。在上世紀70年代Schema作為Lisp相同的資料表示策略。在上世紀80年代,SML增加了unboxing with tuple,堆分配作為一個單一的記憶體塊。在上世紀90年代,OCaml稍微加了點unboxing的浮點陣列。Haskell增加了一些unboxing資料的能力。

但到目前為止,沒有任何函式程式語言預設使用unboxing tuple。即使F#,基於.NET提供任意的值型別,仍然使用.NET的boxed tuple。因此,所有的現代的函式性程式語言產生很高的分配率(allocation rate)基本上沒有很好的理由。因此,他們對垃圾收集器產生的壓力遠遠超過普通必要的壓力水平。這是一個嚴重的問題,不只是因為它使序列程式碼變慢,而且因為垃圾收集器是一個共享的資源,因此,對GC施加了過多壓力會阻礙並行程式的可擴充套件性

命令式集合通常更快的,函式式語言想在效能上超過命令式集合的效能很難,前者變成後者的天花板。

7. 純函數語言程式設計在理論上並行概念很好,但是實踐中效能不行,而效能是使用並行的唯一目的。
今天編寫並行程式有兩個目的:首先實現客觀上更有效率的目的,其次使得本來很慢的方案變得不那麼慢,大多數情況下,函數語言程式設計中的並行屬於後者。在高效能運算領域幾乎沒有人直接執行函式式程式碼,當大多數函式程式設計師使用並行程式設計並不是為了獲得最快的效能,而是為了能在原有效能基礎上有所提升。

像Haskell純函式式語言被設計成空間和時間的概念,這能讓你從更高層次和視角看待你的問題,但是也產生大量記憶體消耗和需要很長時間獲得結果。

注意:人們只談論可擴充套件性而否定絕對效能, 其實絕對效能和可擴充套件性都很重要。


8. 函數語言程式設計很難解決實際問題( It took 50 years for normal people to dilute the smug weenies to the point where you can get a useful answer about functional programming on social media.)
我在函數語言程式設計領域有20年,幾十年來,函式式程式設計師和解決實際問題之間存在鴻溝,感謝這些問題因為Scala, Clojure 和 F# 等出現了改善,但是確實多年來存在蠢貨主導了函數語言程式設計領域的場景,使得人們使用它很難來解決實際問題,比如一些LISP社群一直在解釋Lisp一些引數為什麼是好的,但是經過很多年我才發現這些引數是錯誤的。

9. 大量有關函數語言程式設計的誤傳。
如果你批評Haskell的雜湊表效能,你會得到錯誤的指導,比如有人建議你關閉垃圾回收。

多年來,函數語言程式設計社群炫耀它們簡短的篩選法和快速排序演算法的實現,其實他們並沒有真正落地執行這些演算法。

為什麼Haskell在業界很少使用徹底揭露了Haskell的行業真相。

原文:

The Flying Frog Blog: Disadvantages of purely func

相關文章