Lisp 的爭議
由於 Lisp 語言的 “過於靈活而神秘存在” 的特性使得 Lisp 成了世界上最受爭議的程式語言,實際上獨樹一幟的 Lisp 也在(針對不同的產品,總有熱衷「語言比較」的人們引發語言優勢性的爭論)類的問題得到庇護,因為 Lisp 語言本身不是針對開發專案而誕生的語言,這種性質完美的避開了針對專案型別而友好的語言之間的比較話題。儘管如此,還有很多人用「Lisp 永遠成不了程式設計主流語言」來反駁極少 Hacker 所說的「Lisp 是 Hacker 們最好的神器」。
雖然 「Lisp 永遠成為不了主流的程式語言」這個問題本身就是偽命題,因為 Lisp 從來沒打算成為主流的程式語言,Lisp 與眾不同的部分原因是,它被設計成能夠自己進化。你能用 Lisp 定義新的 Lisp 運算子。當新的抽象概念風行時(如物件導向程式設計),我們總是發現這些新概念在 Lisp 是最容易來實現的。Lisp 就像生物的 DNA 一樣,雖然 Lisp 沒打算成為主流語言,但這樣的語言永遠不會過時。
接下來我們還是引用兩篇文章進行說明:
- 「Lisp 永遠成不了程式設計主流語言」
- 「為什麼 Lisp 沒有流行起來」
Lisp 永遠成不了程式設計主流語言
Lisp 語言是第二古老的高階程式語言。許多的駭客和開發者對 Lisp 推崇備至,Paul Graham 甚至說 “程式語言現在的發展,不過剛剛趕上 1958 年 Lisp 語言的水平”。
然而這樣先進的語言在現在使用的程式語言從來沒有排到前20,聽說它的人不少,用的人卻非常少。
許多人對 Lisp 語言的第一印象就是一層層的括號,很老的關於蘇聯駭客偷到 Lisp 原始碼的最後一頁全是括號的笑話就不用再說了。
造成 Lisp 程式如此多括號的原因就是 「S表示式」。所謂 S表示式,是指一種以人類可讀的文字形式表達半結構化資料的約定,是點對錶示法的形式定義。
S表示式 是 Lisp 語言的鮮明特點,使資料和程式碼形式統一,讓使用者有能力對程式和資料進行統一處理。
Lisp 語言使用這統一的 S表示式,讓 A+B 變成了 (+ A B),資料是統一了,卻讓人彆扭了,尤其在使用更復雜的四則混合運算時更讓人難以接受。然而那些 Lisp 擁護者對這些不能接受 S表示式 的人總是持批評鄙視的態度。
Lisp 未能成為主流的根本原因是這一語言是反人性的,它的先進是對於機器的先進,就像二進位制對於計算機來說是先進的一樣。
人是生物,對事物的需求都有著多樣性的需求,人類的所有語言對漂亮的形容詞從來不止一個,對顏色的要求從來就不止黑白亮色,所以在數字上選擇了十進位制而不選擇二進位制,這是最基本的人性。Lisp 使用 S表示式 抹平了一切多樣性,禁止人類數千年來不約而同選擇的的 A+B 這樣的中綴表示式規則,違反了人性,所以受到了廣大開發者的不接受。
簡單說,Lisp 語言違反了人類人性中對事物多樣性的需求而不能成為程式語言中的主流。
為什麼 Lisp 沒有流行起來
很久以前,這種語言站在電腦科學研究的前沿,特別是人工智慧的研究方面。現在,它很少被用到,這一切並不是因為古老,類似古老的語言卻被廣泛應用。
其他類似的古老的語言有 FORTRAN
、 COBOL
、 LISP
、 BASIC
、 和 ALGOL
家族,這些語言的唯一不同之處在於,他們為誰設計。FORTRAN是為科學家和工程師設計的,他們在計算機上程式設計的目的是是為了解決問題。
COBOL 是為了商業設計的,最好的體現在於讓商人們可以利用電腦時代。LISP是了電腦科學研究設計的,最突出的體現在計算機基本原理研究。BASIC是為初學者設計的。
最後,ALGOL 語言是有計算機程式設計師修改,演變成其他流行的語言,如C,Pascal 和 Java 的一個龐大的家族。上面提到的某些語言已經不像當初那麼流行了。我們在這裡可以把它們稱作 “失敗”。
問題是它們為什麼失敗?第一站出來的是 COBOL,很不幸,它以面向商業人員的很好的可讀性就是它的失敗點。商業人員發現,他們可以僱傭程式設計師去管理他們的系統。程式設計師自然會偏向於為他們設計的語言,而不是他們的老闆。
所以隨著時間推移,越來越多的商業功能都使用例如 VB,C,C++ 和 JAVA 實現了。現在,只有很少一部分軟體仍透過 COBOL 語言編寫。BASIC 卻有不同的命運。他是為入門人員設計的。那些在微機上學習程式設計,他們會使用內建的 BASIC 語言作為起點。
隨著時間推移,微機被執行微軟作業系統的個人電腦,或者 MacOS 的蘋果電腦所代替。這種語言逐漸被 VB 所取代。雖然他是面向初級程式設計師,它有一段時間代替了 COBOL。為什麼要耗費這麼多的資源在昂貴的編譯器上,而便宜的直譯器在我們的電腦上已經存在?
最近,微軟以遷移到 .NET 框架上,讓 VB 跟在後面。它的替代者, C# 就是 ALGOL 家族中的一員,跟 Java 相近。這些年 FORTRAN 的使用起起伏伏。在某一階段,差不多所有科學方面的程式碼是用它來寫的。它的優點是這門語言中沒有指標,並且不允許存在遞迴。
這意味著所有資料的引用位置都可以在編譯時確定。FORTRAN 編譯器 利用這些額外的資訊使程式執行格外地迅速。不幸的是,隨著時間的推移,固定大小的陣列這種資料結構變得過時了。現在,科學要處理任意形狀的風格,甚至表述更為複雜的真實世界。
這需要在語言中額外地加入指標。這些情況發生的時間段裡,FORTRAN 逐漸走向沒落。現在,它被轉移到高效能運算工作,其中新的並行矩陣和向量運算最近新增到這門語言中,仍然使它擁有效能優勢。ALGOL 語言家族取得了成功。
其原因是,這些語言是由程式設計師為程式設計師寫的。隨著時間的推移,這些與系統和應用相關的語言成為了現在最常用的語言。它的優點是越多地程式設計師使用,這門語言就能得到更多地改進,並且越來越多地程式是用它們來寫就的。這提供了一個良性迴圈,更多的程式設計師們又被聘請在己編寫的程式上工作。
這是一個網路效應的例子。一個系統的 “價值” 是它的使用者數目的平方,在於以此速率增長的使用者之間的互動作用。那麼為什麼Lisp語言家族會站在失敗者一邊呢?有些人認為是語法的錯。Lisp 因為它的括號而臭名昭著。我並不認為是這個理由。許多使用者說良好的格式可以讓他們跟上這些括號。
同時,Lisp 語言被發明不久後,有一個叫 “super-bracket” 的語法可以讓人快速表示出任意數量的回括號 “ ) ” 。這個特性在今天已經很少有人使用了。最後,優秀的編輯器解決了大多數的語法問題。另一些人經常抱怨 Lisp 是一門函式式語言。這是失敗的理由嗎?自然,跟早期的語言相比,只有 Lisp 算是函式式的。但事實上,我認為沒有這麼簡單。Lisp 也有命令式語言的特性,ALGOL 系列語言也可以被當作一門純正的函式式語言來用。
如果有人想選擇一種特定的程式設計正規化來寫程式碼,一些特定的語言可以讓這個選擇更容易的實現。然而,現代語言已經足夠靈活,它們能支援多種程式設計正規化,近乎完全命令式的Lisp沒有理由不存在。或許lisp的問題在於他使用了垃圾回收?
在那個時候,只有 Lisp 作為計算機語言採用了這個特性。誠然,垃圾回收會佔用大量的計算資源,而早期計算機在該方面的不足足以組織 Lisp 大展拳腳了。但是,我認為這仍然不是主要的原因。
Lisp 是用來寫那些複雜度相當高的程式的,而這些程式在事實上都必須帶有一個垃圾回收模組,如果你用其他的語言來寫…… 大概很難比 Lisp 實現的要好吧?眾所周知的事實是,任何一個如此複雜的程式,如果用其他語言寫的話都不可避免的戴上一個比 Lisp 垃圾回收臃腫不少的功能模組…… Lisp 的失敗,恰恰是因為他太成功,這讓他的目標變得模糊。Lisp相對與早期的語言實在是非常靈活,靈活到足以改變自身形式以適應需求。對於其他的語言來說,如果想要完成一個龐大的任務,就需要把這個任務打碎成一小塊一小塊的然後完成。
如果是一個更大的呢?甚至連編譯都需要分步完成了。但是 Lisp 不是這樣的,由於他強大的能力,程式設計師可以將 Lisp 改造成特定領域的專門工具——順手的工具將順手的解決問題——任務輕鬆完成了。
由於語言的正交性(譯者注:這裡可能應該理解為 “自洽” ),我們改造過的 Lisp 仍然可以使用原有的編譯器,直譯器執行。那麼建立特定領域的語言來作為一個問題的解決方案,它會出現什麼問題呢?
結果是它非常高效。然而,這種做法會使語言分化。這導致許多子語言都略有不同。這是 Lisp 程式碼對其他人而言可讀性差的真正原因。在其他語言中,相對來說比較簡單就能臆測出一段給定程式碼的作用。
有著超強的表達力的 Lisp,由於一個給定的符號 (symbol) 可能是一個變數,函式或操作,需要閱讀大量程式碼才能找出它。Lisp 失敗的原因是因為它的碎片化,並且它的碎片化是因為其語言天性與特定領域方案的風格造成的。
而網路效應則恰恰相反。越來越少的程式設計師使用相同的方言,因此它相對與 ALGOL 語言家族的總價值下降。如果有人現在設計一種語言,該如何避免這種問題呢?如果語言的表達性是我們的目標,那麼它必須以某種方式加以調整。
這門語言必須要有特意的限制,來保證所編寫程式碼的可讀性。Python 是一門成功的語言,它已經做到了這些,其中某些限制是硬編碼的,而另一些則是以約定成俗的方式存在。不幸的是,這麼久過去了並且發明了這麼多 Lisp 的變種語言,在其之上建立的其它新語言大概並不是所要的答案。
根本不會有足夠多的使用者使它與眾不同。也許解決的辦法是,慢慢加入類似 Lisp 的語言功能到 ALGOL 語言家族中。幸運的是,這似乎是正在發生的事。新的語言(C#
,D
,Python
等)趨向於擁有垃圾回收機制。他們也往往比舊的語言更具正交性。
Lisp 語法反人性中的隱藏寶藏
如果你剛開始學習的時候,主要是忍受著它那字首表示式和大量的括號,去學習它的函數語言程式設計,這時候沒覺得 Lisp 有多厲害,等到學到了 宏(macro)這個主題的時候,就被 Lisp 的變態的能力真正地震撼了:
這不是一門簡單的程式語言,它是一門創造其他語言的語言。
Lisp 在製造別的語言(DSL)時是如此的成功,以至於它最終走向了 “失敗”。
很多語言解決問題的思路是分而治之:把一個大任務拆分成一個個小任務,再把小任務拆分成更小的任務,然後去編碼實現。
Lisp 則有著截然不同的思路,由於其強大的能力,Lisp 程式設計師傾向於改造 Lisp,把這門語言改造成一個問題領域相關的語言,即 DSL ,然後用這個 DSL 來輕鬆程式設計,去解決問題。
當然這個改造的過程是個漸進式的:
“在程式設計的時候你可能會想 'Lisp 要是有這樣或者那樣的運算子就好了。' 那你就可以直接去實現它。之後,你會意識到使用新的運算子也可以簡化程式中另一部分的設計,如此種種。語言和程式一同演進。就像交戰兩國的邊界一樣,語言和程式的界限不斷地移動,直到最終沿著山脈和河流確定下來,這也就是你要解決的問題本身的自然邊界。最後你的程式看起來就好像語言就是為解決它而設計的。並且當語言和程式彼此都配合得非常完美時,你得到的將是清晰、簡短和高效的程式碼。”
--- Paul Graham 《On Lisp》。
這種使用DSL去解決問題的方法有什麼問題呢?
-
碎片化! 會出現很多“小語言”,這些語言之間有細微的不同,這就是為什麼你的 Lisp 程式碼對別人來說讀起來很吃力的原因。
-
而其他語言則不存在這個問題,相對容易去理解程式碼的含義。Lisp, 由於其變態的表達能力, 一個符號可能是個變數,函式,運算子。你需要花費大量時間去閱讀程式碼才能搞清楚它到底是什麼含義,這就太悲催了。
-
Lisp 的 “失敗” 的一大原因就是它的碎片化,而碎片化又源於語言本身的特性和它那用 DSL 解決問題的風格。
我在說 “失敗” 的時候,一直用引號, Lisp 真的失敗了嗎?No!Lisp的思想已經進入到了現在主流的語言中,無論是 Python,JavaScript,甚至 Java 都具備函數語言程式設計的能力,還有像 Scala 這樣既能OOP,又能 FP 的語言。
但是,Lisp 那強大的宏,那執行時改變自身的能力並沒有被其他語言接受,Ruby 比較接近,但是差得還很遠。可能大家害怕這個雙刃劍了吧!
本作品採用《CC 協議》,轉載必須註明作者和本文連結