新聞中心
之前寫了那么多 Haskell 的不好的地方,卻沒(méi)有提到它好的地方。其實(shí)我必須承認(rèn),我從 Haskell 身上學(xué)到了非常重要的東西,那就是對(duì)于“類型”的思考。雖然 Haskell 的類型系統(tǒng)有過(guò)于強(qiáng)烈的約束性,從一種“哲學(xué)”的角度看感覺(jué)“不自然”,但如果一個(gè)程序員從來(lái)沒(méi)學(xué)過(guò) Haskell,那么他的腦子里就會(huì)缺少一種重要的東西。這種東西很難從除 Haskell,ML,Coq,Agda 以外的其它語(yǔ)言身上學(xué)到。

10年積累的成都網(wǎng)站設(shè)計(jì)、成都網(wǎng)站建設(shè)經(jīng)驗(yàn),可以快速應(yīng)對(duì)客戶對(duì)網(wǎng)站的新想法和需求。提供各種問(wèn)題對(duì)應(yīng)的解決方案。讓選擇我們的客戶得到更好、更有力的網(wǎng)絡(luò)服務(wù)。我雖然不認(rèn)識(shí)你,你也不認(rèn)識(shí)我。但先網(wǎng)站制作后付款的網(wǎng)站建設(shè)流程,更有敦煌免費(fèi)網(wǎng)站建設(shè)讓你可以放心的選擇與我們合作。
Haskell 教會(huì)我的東西
一個(gè)沒(méi)有學(xué)過(guò) Haskell 的 Scheme 程序員,最容易犯的一個(gè)錯(cuò)誤就是,把除 #f(Scheme 的邏輯“假”) 以外的任何值都作為 #t(Scheme 的邏輯“真”)。很多人認(rèn)為這是 Scheme 的一個(gè)“特性”,可是殊不知這其實(shí)是 Scheme 的極少數(shù)缺點(diǎn)之一。如果你了解 Lisp 的歷史,就會(huì)發(fā)現(xiàn)在最早的時(shí)候,Lisp 把 nil(空鏈表)這個(gè)值作為“假”來(lái)使用,而把 nil 意外的其它值都當(dāng)成“真”。這帶來(lái)了邏輯思維的混亂。
Scheme 對(duì) Lisp 的這種混亂做法采取了一定的改進(jìn),所以在 Scheme 里面,空鏈表 '() 和邏輯“假”值 #f 被劃分開來(lái)。這是很顯然的事情,一個(gè)是鏈表,一個(gè)是 bool,怎么能混為一談。Lisp 的這個(gè)錯(cuò)誤影響到了很多其它的語(yǔ)言,比如 C 語(yǔ)言。C 語(yǔ)言把 0 作為“假”,而把不是 0 的值全都作為“真”。所以你就看到有些自作聰明的 C 程序員寫出這樣的代碼:
- int i = 0;
- ...
- ...
- if (i++) { ...}
Scheme 停止把 nil 作為“假”,卻仍然把不是 #f 的值全都作為“真”。Scheme 的崇拜者一般都告訴你,這樣做的好處是,你可以使用
- (or x y z ...)
這樣的表達(dá)式,如果其中有一個(gè)不是 #f,那么這個(gè)表達(dá)式會(huì)直接返回它實(shí)際的值,而不只是 #t。
然而他們沒(méi)有看到的是,其實(shí)這個(gè)表達(dá)式所要達(dá)到的“目的”,其實(shí)有更加簡(jiǎn)單而直接的方法,而不需要把非 #f 的值都作為“真”。你只需要定義一個(gè)函數(shù):
- (define orf
- (lambda (ls)
- (cond
- [(null? ls) #f]
- [else
- (let ([v (car ls)])
- (if (not (eq? v #f))
- v
- (orf (cdr ls))))])))
之后你就可以這樣調(diào)用它:(orf '(#f #f 0 #f "foo"))。這會(huì)在遇到 0 的時(shí)候返回它,因?yàn)?是這個(gè)鏈表里第一個(gè)不是 #f 的值。如果鏈表里全都是 #f 它就返回 #f。
這比起 Scheme 的 or 來(lái),不但效率一樣,而且還有一個(gè)好處。那就是這個(gè) orf 是一個(gè)函數(shù),而 or 是一個(gè)宏。所以你沒(méi)法把 or 作為參數(shù)傳遞給另一個(gè)函數(shù)。你沒(méi)法使用像 (map or ...) 這樣的寫法。而這個(gè) orf 由于是一個(gè)函數(shù),所以可以被作為值,任意的傳遞給另一個(gè)函數(shù)。
Haskell 的類型系統(tǒng),就是幫助你嚴(yán)密的思考關(guān)于類型的問(wèn)題的??墒?Haskell 做得過(guò)分了一點(diǎn),由于對(duì)類型推導(dǎo),一階邏輯和 category theory 等理論的盲目崇拜,Haskell 里面存在很多不必要的復(fù)雜性。各種各樣的類型推導(dǎo)我寫過(guò)不下十個(gè),其中有一些比 Haskell 強(qiáng)大很多。我設(shè)計(jì)了自己的類型系統(tǒng)。category theory 其實(shí)不是什么有用的東西。很多數(shù)學(xué)家把它叫做“abstract nonsense”,就是說(shuō)它太“通用”了,以至于相當(dāng)于什么都沒(méi)說(shuō)。我曾經(jīng)在一個(gè)晚上看完了整本的 category theory 教材,發(fā)現(xiàn)里面的內(nèi)容我其實(shí)通過(guò)自己的動(dòng)手操作(實(shí)現(xiàn)編譯器,設(shè)計(jì)類型系統(tǒng)和靜態(tài)分析等等),早就明白了。
所以我不想再使用 Haskell,我對(duì)它的程序員的“天才態(tài)度”也感到厭倦,然而我的腦子里卻留下了它教會(huì)我的東西。對(duì) Haskell 的理解,讓我成為了一個(gè)更好的 Scheme 程序員,更好的 Java 程序員,更好的 C++ 程序員,甚至更好的 shell 腳本程序員。我能夠在任何語(yǔ)言里再現(xiàn) Haskell 的編程方式的精髓。然而讓我繼續(xù)用 Haskell ,卻就像是讓我坐牢一樣。本來(lái)很簡(jiǎn)單的事情,到 Haskell 里面就變成這樣那樣莫名其妙的新術(shù)語(yǔ)。Haskell 的設(shè)計(jì)者們的論文我大部分都看過(guò),幾分鐘之內(nèi)我就知道他們那一套東西怎么變出來(lái)的,其實(shí)里面很少有新的東西。大部分是因?yàn)?Haskell 引入的那些“新概念”(比如 monad)而產(chǎn)生的無(wú)須有的問(wèn)題。世界上有比他們更聰明的人,更簡(jiǎn)單卻更強(qiáng)大的理論。不要以為 Haskell 就是世界之巔。
所以怎么說(shuō)呢,我覺(jué)得每個(gè)程序員的生命中都至少應(yīng)該有幾個(gè)月在靜心學(xué)習(xí) Haskell。學(xué)會(huì) Haskell 就像吃幾天素食一樣。每天吃素食顯然會(huì)缺乏某些營(yíng)養(yǎng),但是每天都吃葷的話,你就永遠(yuǎn)意識(shí)不到身體里的毒素有多嚴(yán)重。
專攻一門語(yǔ)言的害處
我曾經(jīng)對(duì)人說(shuō) C++ 里面其實(shí)有一些好東西,但是我沒(méi)有說(shuō)的是,C++ 里面的壞東西實(shí)在太多了。
有些人從小寫 C++,一輩子都在寫 C++。這樣的結(jié)果是,他們對(duì) C++ 里面的“珍珠”掌握的非常牢靠,以至于出現(xiàn)了一種“腦殘”的現(xiàn)象——他們沒(méi)法再寫出邏輯清晰的程序。(這里“珍珠”是一個(gè)特殊的術(shù)語(yǔ),它并不含有贊美的意思。請(qǐng)參考這篇博文。)
比如,很多 C++ 程序員很精通 functor 的寫法,可是其實(shí) functor 只是由于 C++ 沒(méi)有 first-class function 而造成的“變通”。C++ 的 functor 永遠(yuǎn)也不可能像 Scheme 的 lambda 函數(shù)一樣好用。因?yàn)槊看涡枰粋€(gè) functor 你都得定義一個(gè)新的 class,然后制造這個(gè) class 的對(duì)象。如果函數(shù)里面有自由變量,那么這些自由變量必須通過(guò)構(gòu)造函數(shù)放進(jìn) functor 的 field 里面,這樣當(dāng) functor 內(nèi)部的“主方法”被調(diào)用的時(shí)候,它才知道自由變量的值。所以為此,你又得定義一些 field。麻煩了這么久,你得到的其實(shí)不過(guò)是 Scheme 程序員用起來(lái)就像呼吸空氣一樣的 lambda。
這些“精通” functor 的 C++ 程序員,認(rèn)為會(huì)用 functor 就說(shuō)明自己水平高。殊不知 functor 這東西不但是一個(gè)“變通”,而且是從函數(shù)式語(yǔ)言里面“學(xué)”過(guò)來(lái)的。在最早的時(shí)候,C++ 程序員其實(shí)是不知道 functor 這東西的。如果你考一下古就會(huì)發(fā)現(xiàn),C++ 誕生于 1983 年,而 Scheme 誕生于 1975 年,Lisp 誕生于 1958 年。C++ 的誕生比 Scheme 整整晚了8年,然而 Scheme 一開始就有 lexical scoping 的 lambda。functor 只不過(guò)是對(duì) lambda 的一種繞著彎的模仿。實(shí)際上 C++ 后來(lái)加進(jìn)去的一些東西(包括 boost 庫(kù)),基本上都是東施效顰。
記得2011年11月11日的良辰吉日,C++ 的創(chuàng)造者 Bjarne Stroustrup 在 Indiana 大學(xué)做了一個(gè)演講,主題是關(guān)于 C++11 的新特性。當(dāng)時(shí)我也在場(chǎng),主持人 Andrew 是 boost 庫(kù)的首席設(shè)計(jì)師之一(他后來(lái)有段時(shí)間當(dāng)過(guò)我的導(dǎo)師)。他連夸 Stroustrup 會(huì)選日子,只是遺憾演講時(shí)間沒(méi)有定在11點(diǎn)。
雖然我對(duì) Stroustrup 的幽默感和謙虛的態(tài)度感到敬佩,但我也看出來(lái) C++11 相對(duì)于像 Scheme 這樣的語(yǔ)言,其實(shí)沒(méi)有什么真正的“新東西”。大部分時(shí)候它是在改掉自己的一些壞毛病,然后向其它語(yǔ)言學(xué)習(xí)一些東西,然后把這些學(xué)習(xí)的痕跡掩蓋起來(lái)。可是到最后,它仍然不可能達(dá)到其他語(yǔ)言那么原汁原味的效果。然而,由于 C++ 的普及程度之高,現(xiàn)成的代碼之多,它的地位和重要性還是一時(shí)難以動(dòng)搖的。所以“先輩的罪”,我們恐怕要用很多代人的工作才能彌補(bǔ)。
那么 C++ 有什么其他語(yǔ)言沒(méi)有的好東西呢?其實(shí)非常少。我還是有空再講吧。
多學(xué)幾種語(yǔ)言
我今天想說(shuō)其實(shí)就是,沒(méi)有任何一種語(yǔ)言值得你用畢生的精力去“精通”它。“精通”其實(shí)代表著“腦殘”。你必須對(duì)每種語(yǔ)言都帶有一定的懷疑態(tài)度,而不是完全的擁抱它。每個(gè)人都應(yīng)該學(xué)習(xí)多種語(yǔ)言,這樣才不至于讓自己的思想受到單一語(yǔ)言的約束,而沒(méi)法接受新的,更加先進(jìn)的思想。這就像每個(gè)人都應(yīng)該學(xué)會(huì)至少一門外語(yǔ)一樣,否則你就深陷于自己民族的思維方式。有時(shí)候這種民族傳統(tǒng)的思想會(huì)讓你深陷無(wú)須有的痛苦,卻無(wú)法自拔。
網(wǎng)頁(yè)標(biāo)題:王垠:關(guān)于編程語(yǔ)言的思考
文章網(wǎng)址:http://m.fisionsoft.com.cn/article/dhehjps.html


咨詢
建站咨詢
