怎麼看待王垠對 Haskell 的評價?

《不再推薦 Haskell》 http://blog.sina.com.cn/s/blog_5d90e82f0101b37r.html


我能給python找出一大堆問題(比如GIL), 也能給Javascript找出一大堆問題(比如命名空間),也能給Java找出一大堆問題( 太多不說了,諸如 maven之類的感覺只是在彌補語言缺陷)。

問題是,即便這樣,我們依然在添添補補的狀態下,去用它們,即便是PHP我們報以寬容的態度。

於是面對這麼一個pure的,純粹的語言,我們就從實用的角度上,要刀戈相向?

越是純粹的東西,往往越是無用。越是有用的東西,往往需要從那些純粹的理想國裡面東摘西取,來彌補自身的不足使自己更加有用,然後愈發地不純粹。


其實haskell確實是一個入門比較困難的語言,所以有人說不推薦初學者去學習我認為是非常可以理解的。

象他提到的類型推理不利的問題我已經遇到過很多次了,完全相同的代碼只是增加類型說明就可以編譯通過。另外我也試過和另外語言去比較(python)同一任務haskell需要寫更多的代碼,但是那是因為haskell不做預設邏輯(主要是針對輸入為空等情況)。

但是個人還是覺得haskell是值得來學習的語言,

首先就是它的獨特性:現在惰性和純函數式的語言本就很少,接觸這樣的語言還是非常有利於學習者了解編程世界的廣闊性,學習最主要的目的之一不就是開拓視野么?而且pure的語言可以逼迫編程者修改他的設計思路,讓他考慮如何把交互和pure演算法分離,而之前我個人很少有過這個方面的考慮,因此代碼都是隨機揉合的。

其次是嚴謹性:就如前面的回答者說的,嚴謹和便利不可兼得,當時寫一個很簡單的字元串分段排序別人用python寫出比我用haskell更加短。但是為什麼可以做到呢?那是因為python用自己默認的邏輯處理了諸如空字元串這樣的異常場景,而haskell完全需要手工去寫。語言不幫你增加隱含的邏輯處理當然是不方便的,不過學習這樣的語言可以讓你更加自然的想到要考慮更多的邏輯細節。

最後我想說的是,之前看過一個採訪視頻,是ghc的設計師。他的說法就是haskell師一個「理想國」似的前沿試驗場,他會從這裡吸取養分和教訓,然後落地到其他一些更加「實用」的語言中去。從這個層面來說通過學習haskell知道它有些什麼不足和缺陷也是非常有價值的,只有親身體驗才能避免被別人忽悠


我還是覺得,學Haskell對了解很多編程語言的概念是有用的。對於一個從來沒接觸過這個領域的人來說,Haskell顯然是一個很好的工具。一旦你覺得你入門了,那以後再怎麼樣就是你的事情了。當然,對於那些「學XX就是為了用XX來工作」的投機分子來講,就忽略我的話好了。


王垠的這篇文章並不適合作為初學者的參考。因為王的理由涉及PLT學術爭端,而爭端必然有不同的意見,而王垠的文章只有他自己的一面說法。對於這些學術爭端,也只有你去深入學習了解,才可能明白。這樣初學者就陷入一個悖論,如果你要分析評價王的文章,必然需要對haskell和type theory有一定了解。但你如果聽王垠的結論,則又放棄在這兩方面的學習。

所以,把王垠文章涉及的理論關鍵字挑出來自己去了解,忽略他(和上面其他人)的結論。


從java走到haskell的世界,會發現原來類型系統的表達能力可以這麼強。或許有一天我也會深入到王垠的程度,但現在我還是推薦從haskell入手學習函數式編程,它會逼著你掌握函數式編程的方方面面,沒有退路。 至於使用其實可以選其他語言,像我就是學著haskell,用著scala。


認真看完了,感覺應當算是對現在大紅大紫的函數式編程的一個比較冷靜和深入的思考吧。

對於函數式編程,我學過一些Lisp和erlang,沒有實際實踐過項目,如果有不對請指正。

首先先說說類型標記,我非常贊同他的觀點(參數和返回值有類型,局部變數無類型),使用類型對變數的性質進行約束,雖然增加來書寫聲明的不便,但是也可以為程序的排錯帶來很多遍歷。雖說鴨子類型是一種很好的做法,可是我覺得當一個程序變得比較複雜的時候,我們並不一定清楚鴨子長什麼樣,或者我們不能保證我眼中的鴨子和你眼中的鴨子是一樣的。嚴謹和便捷總是那麼格格不入。

動態類型的缺點主要有三個,即在執行時才能發現錯誤、讀程序時可用到的線索少,以及運行速度慢。

首先執行時才能發現錯誤這一點可以用完備的單元測試來解決。如果能嚴格實行完備的單元測試的話,即使沒有編譯時的錯誤檢查,程序的可靠性也不會降低。其次,讀程序時可用到的線索少這一點可以通過完整的文檔來解決。Java有JavaDoc技術,Ruby也有RDoc技術,可以在源代碼中同時寫文檔,減輕維護文檔的負擔。

松本行弘《松本行弘的程序世界》

對於函數式編程,我一直懷疑類似像無狀態、一次賦值這樣的要求,是不是真的能用來解決問題,而且這些要求也與帶有狀態的計算機基本模型——圖靈機——也是格格不入的。

如果程序設計都用這樣的語言(注1)完成,形式描述的需要性也就不會象現在這樣迫切了。然而,在計算機技術的目前狀況下,由於效率上的原因,這些語言還不能廣為傳播供實際使用。說得根本一些,這些語言是否會通用還值得懷疑,因為它們往往忽略了一個困難而又不可避免的程序設計的特徵,即軟體工程和純數學間的本質區別,因而需要改變問題求解的表述,使得他們能夠在物理硬體上高效地運行。

研究一下Lisp和Prolog的實際使用是有益的。它們是含較少命令特徵而又得到足夠廣泛使用的僅有例子(注2)。但是,真正實際有用的Lisp和Prolog程序(而不是教科書上的例子)似乎又經常使用命令式特徵,如Lisp的PROG,SETQ和RPLACA(它們對應顯式的執行次序、賦值和副作用),以及Prolog的cut機制(非正規的執行次序)。雖然這個現象還需要進一步調查,但是我們懷疑,性能上的限制不是命令式構造讓然存在的唯一原因。

陳意雲《形式語義學》1.2.3

注1:指的是該節所提到的「非命令式的語言」,即函數式語言。注2:該書寫作於1993年,Haskell、erlang等如今流行的函數式編程語言當時還沒有發明或流行。

當然,函數式編程語言還是對現代編程語言起到了推動的作用,Lambda表達式也幾乎成為現代語言的標配了。作者最後一句說得好。

所以現在我覺得,其實世界上的語言並沒有什麼絕對的標準。在一段時間認為是錯的東西,可能卻是對的。所以不用盲目的排斥一些語言,把它們都拿來看一下,互相對比,不時的走出自己的圈子,才會知道到底什麼是好的。


作為一個老牌 Haskell 黑,我得說王垠的看法我基本認同。五六年前,我就一直說Haskell的語法和類型系統是走了一條不求解決問題但求彰顯思想的邪路。對一個嚴肅的工程師來說,如果目的不是學習而是工程,那麼Haskell不是他/她應該去關注的語言和社區。如果真的關注實用化函數式程序設計,Scheme是更好的選擇。

不過反過來說,我認為咱們大多數人在類型系統的研究上根本達不到王垠的深度,他的很多指責恐怕很多人都不會真的理解。比如他說的惰性求值和並行計算的矛盾,我就不甚了了。站在這個立場看,我的態度是:

  1. 如果你已經選擇了Haskell,那就接著學好了。如果你想捍衛你心目中的完美語言,起碼得比你的對手更了解它。
  2. 如果你還沒選擇Haskell,而你認為王垠的學識是你信得過的,那麼我建議你遠離Haskell。世上程序設計語言很多,沒必要在一棵樹上弔死。
  3. 如果你還沒選擇Haskell,而你認為王垠的學識你又信不過,那麼就看你自己有沒有時間去學。這裡沒有信條沒有宗教,做自己的決定比信什麼結論都重要。


這個問題我是這樣看的,fp有它的歷史階段,只是當時還沒有達到這個階段而已

凡事不能脫離context來看

以fp要求的純函數,無副作用為例,這個並不難實現,在生產中大多數的副作用來自io層面,這個問題可以這麼解決,全部用haskell或者lisp來實現,io會很蛋疼,副作用很明顯,需要monad來封裝,而monad又是那麼地難以理解,因為不直觀,對吧?也就是純粹用fp做,會有些困難

那腫么辦?遇到問題不要逃避,想辦法解決問題

可以這麼做嘛,沒有人規定haskell或者lisp的框架只能用這兩個語言實現

就猶如沒有人規定java的jvm只能用java來寫一樣,你可以用c來寫jvm,當然java的標準庫大部分還是java實現的,但是jvm大部分是c/c++實現的

同樣道理,沒有人規定os只能用c來寫,所以有些時候,你還是需要彙編來直接對機器下指令

那很簡單了,我們能不能用java等oop語言來實現一個框架,然後給到pure fp語言去用不就好了?

為什麼不行?而這就是我們正在實踐的東西

vert.x已經做到的一點是把所有的io全部封裝起來,提供出純粹的fp的介面出來

這個是我們在實踐中發現的一個進步,就是我們一開始在vert.x上使用java,用groovy,用js甚至用ruby,都比較順利,但是我們發現一點,就是腳本語言也能實現verticle之後,我們還需要把邏輯封裝成class嗎?放到class裡面去嗎?似乎不需要,因為腳本語言已經展示給我們這種做法了,為什麼還需要這樣封裝呢?

換句話說,我們全部封裝成函數行不行?

那這個時候rxjava等reactivex告訴我們,這是可以的,實際上在vert.x 2的階段,就已經有了一個pure fp語言的支持,就是lisp的一個方言clojure的支持,我們回頭去看vert.x 2的clojure的實現

你會發現,純函數,immutable等並不是不可能,相反,是完全可行的

那為了進一步驗證我們的觀點,我們嘗試使用了kotlin,which是oop+fp的混雜,並嘗試著將代碼全部封裝成fun,也就是kotlin裡面的函數關鍵字,然後發現,誒,好像問題並不大,除了verticle本身還要求繼承abastract verticle以外,其他都ok,沒有什麼太大問題,我們甚至不需要monad,因為kotlin支持不支持monad,說實話我現在還沒有想過,因為沒有用到,也就沒有去探索了

而我們現在探索的是,如果我們把haskell搬到vert.x上去,是不是可以實現用純fp語言在開發上,因為kotlin畢竟不是pure fp的語言,還是很容易寫出各種副作用來,而且vert.x對於kotlin的支持也偏向在class上,雖然kotlin也能寫成pure fp的方式,scala,ceylon類似,但是目前還沒有這麼做,所以現在就在探索,這麼做是否是可行的

eta也就是jvm上的haskell會在v0.2版本中加入vert.x的原生支持

目前vert.x和eta-lang雙方都展示出了興趣

從目前進展看,問題不是特別大,因為vert.x v2已經有了一個pure fp which is lisp/clojure的支持

現在只是把pure fp這個給找回來而已

所以看哈,用c實現os,但是還是離不開彙編,用c++實現jvm,用java實現標準庫,但是還是離不開c,甚至還需要一點點彙編,同樣的,用vert.x來實現一個fp的運行環境,封裝副作用,最後我們用fp來實現我們想要表達的邏輯

os -&> jvm -&> vert.x

這是一個進步,以前沒有jvm的時候,你想用java,用不了,跨平台是必須要解決的問題

同樣,沒有vert.x的時候,你想用haskell/lisp,會很蛋疼,因為io你要自己去解決,很煩

結果大多數時候都在折騰io等副作用,但是如果我們用vert.x將其封裝成函數能夠嵌套的方式

就是reactive方式,那套上函數不就是很自然而然的么?感謝java自身,加上了lambda

所以使得jvm跟pure fp的距離拉近了,變得可行了起來

其實話說回來,spring的編碼方式,就是一種pure fp的編碼方式,只是很多人沒有意識到這一點而已,spring的component不允許有狀態在預設狀況下,這就是一種無副作用的函數,但是spring有著jee上過多的歷史包袱,導致在oop這個領域上有些over engineering,所以為了照顧這些legacy,它可能沒有辦法去嘗試和拓展新的領域,所以哪怕spring十多年前就搞出了oop上的fp環境,還是沒有用,而需要等到actor model在jvm上的general實現也就是vert.x誕生之後,才使得這一步變得可行起來

actor model最早就是通過pure fp語言erlang實踐,然後被scala移植到了jvm上,就是akka項目,再由vert.x將akka和node.js相結合,形成了一種能夠被大多數語言所接受的這麼一個actor model實現,所以目前呢:

vert.x搞腳本=OK,搞OOP=OK,搞OOP+FP=OK

是時候證明vert.x搞FP也=OK的時候了

我們希望實現的目標是:

能夠用pure fp e.g. Haskell實現絕大多數業務邏輯,同時不使用任何的Monad

只要能實現這一步,就基本上可以證明該語言production ready了

production ready是這樣子,不要用太fancy的功能,就像很多spring developer其實根本就不會java,連反射都不會,線程和進程都分不清楚,但不妨礙他填代碼,那對於pure fp來說,我們能實現不懂monad的人一樣能夠使用該語言實現邏輯便可,其實這個幾乎是肯定可以,前面無數的例子證明了這一點,任何一個面向fp的框架都能做到這一點,那我們想做到的一點就是通過oop實現一個fp的runtime,而不是fp實現fp的runtime,同時我們也希望性能不要下降太多,大概是原來的70%以上,最好不要低於原先效率的一半


對於Haskell,我是這樣想的:只有走到A的極端的人,才能真正掌握A。若果不是,那麼就是要麼是片面,要麼是人云亦云的。Haskell是函數式的極端,對比於現界流行的語言,還是很值得學習的。所以對於「不再推薦Haskell"的觀點,我是持相反態度。


王垠說的原話是"可是學習編程總要從某種語言開始。那麼哪種語言呢?就我的觀點,首先可以從 Scheme 入門,然後學習一些 Haskell (但不是全部),之後其它的也就觸類旁通了。"

後來他也覺得Haskell不適合沒有編程經驗的初學者.

對於沒有學過編程的人來說Haskell的確不是一個好的選擇. 但對於有Imperative編程經驗但沒有Funtional Programming經驗的人,Haskell是一門值得學習的非常優美的FP語言.


原文被刪了?!怎麼破?


支持陳甫鵃的答案,選擇傾向於王垠的觀點。本來想學Haskell的,等等吧。我認為,無論學什麼東西,都要以實用性為目標。不利於解決實際問題的編程語言,我不支持。學著玩兒?您開玩笑吧。


不必學。

首先,學一門語言不外乎兩種原因:作為工具,或者學習它的思想。

Haskell作為工具,可以說是沒有用的。因為Haskell患有一種病,叫做「本質不實用性」,意思是不實用是Haskell的一部分,不可改變。造成這種病的原因是Haskell的一些「特性」,包括純函數,惰性求值,Hindley-Milner類型系統。

Haskell的思想固然是有其好處的,然而Haskell的思想中有很多不好的地方(由於篇幅有限,不在這裡列出來)學習之後反而思想會受到限制。

所以,與其學Haskell,倒不如去看看一些關於程序語言理論的書籍,也許就不會被Haskell中的」特性「所迷惑。


推薦閱讀:

如何看待王垠的 《對 Rust 語言的分析》?
如何評價王垠新博文《我看自動駕駛技術》?
曾老師如何看待除了垠神之外其他三大編程天王?
王垠在《程序員的心理疾病》中提到 Python 的缺點是哪些?
王垠的「40 行代碼」真如他說的那麼厲害嗎?

TAG:王垠人物 | Haskell |