Paul Graham:ansi Common Lisp (中文版)

ANSI Common Lisp 主要論點詳解

本文件旨在根據提供的《ANSI Common Lisp 中文翻譯版》前言及目錄,提取並詳細闡釋書中關於 Common Lisp 語言本身及其程式設計哲學的主要論點。書中不僅教授語言特性,更強調 Common Lisp 所蘊含的獨特且強大的程式設計思維與方法。

論點一:Common Lisp 作為一門獨特且強大的語言,賦予程式設計師超越傳統的能力。

Common Lisp 被描述為繼 FORTRAN 之後仍在使用的最古老的程式語言,但其非凡之處在於它始終走在程式語言技術的前沿。作者認為 Lisp 之所以與眾不同,部分源於其被設計為能夠自我演化。這意味著程式設計師可以使用 Lisp 定義新的 Lisp 操作符(Operators)。當新的抽象概念(例如面向對象程式設計)流行時,它們最容易在 Lisp 中實現。這種靈活性使得 Lisp 就像生物的 DNA 一樣,永遠不會過時。

更關鍵的是,Lisp 讓程式設計師能夠實現其他語言難以做到的事情。書中以詞法閉包(Lexical Closure)為例,展示了 Lisp 可以輕鬆返回一個帶有「記憶」的函數(如 addn 函數可以記住創建它時的數字 n 並將其加到任何輸入參數上),這在許多主流語言中是不可實現的,或者實現起來極其複雜。Lisp 程式員頻繁地使用閉包這一抽象概念,使其成為程式設計的常態工具。

另一個 Lisp 獨特且更有價值的特點是,Lisp 程式本身就是由 Lisp 的數據結構(特別是列表)來表示的。這表示程式設計師可以撰寫能夠撰寫程式的程式。這種能力主要體現在巨集(Macros)的應用上。巨集在編譯時執行,接收程式碼作為輸入,並輸出修改或生成的程式碼,從而擴充或改變語言的語法和行為。這使得有經驗的 Lisp 程式員能夠並且經常為自己特定的程式需求量身打造語言。這一能力被視為 Lisp 凌駕於面向對象程式設計之上的重要基礎之一,第 10 章和第 17 章的範例將詳細闡述和證明這一點。

學習 Lisp 因此不僅僅是學習一門新的語言,它更教會程式設計師一種嶄新且更強大的程式思考方法。Lisp 提供的工具和抽象概念(如閉包、巨集、執行期類別等)共同組成了一個關鍵部分,使得一種新的、更自由、更有效率的程式設計方式成為可能。

論點二:Common Lisp 的核心特性促進自底向上程式設計、軟體可擴充性與可重用性。

Common Lisp 的一系列新特性——自動記憶體管理(垃圾回收)、顯式類型(值有類型而非變數)、閉包等——使程式設計變得更簡單。更重要的是,這些特性結合起來,使得一種新的、高度可擴充的程式設計方式得以實現。

Lisp 的設計理念是可擴充的:它允許用戶定義自己的操作符。這之所以可行,是因為 Lisp 語言本身的構造(包括內建函數和巨集)與使用者自己編寫的程式碼使用了相同的形式和機制。擴充 Lisp 就像寫一個 Lisp 程式一樣簡單。這種擴充語言自身的能力非常有用,以至於成為 Lisp 程式設計的標準實踐。當程式設計師使用 Lisp 編程時,他們同時也在為自己的程式創建一個更適合的語言。這種工作方式既是自底向上(從基本元素構建複雜結構),也是自頂向下(從問題需求定義語言特性)。

幾乎所有的程式都能從客製化適合自己所需的語言中受益,而越複雜的程式,自底向上程式設計的價值就越顯著。一個遵循自底向上原則設計的程式,可以寫成一系列層次,每一層都作為其上一層的程式語言。雖然理論上可以用任何語言實現自底向上設計,但 Lisp 本質上最適合這種方法,因為其語言本身的靈活性和可擴充性與這種方法高度契合。

自底向上的編程方法自然地發展出可擴充的軟體。如果將程式設計的最上層視為用戶與之互動的介面,那麼這一層就成為用戶的程式語言。由於可擴充的思想深植於 Lisp 中,使得 Lisp 成為實現可擴充軟體的理想語言。書中提到,三個 1980 年代最成功的程式——GNU Emacs、Autocad 和 Interleaf——都提供了 Lisp 作為其擴充語言,這正是 Lisp 在此方面的成功實例。

此外,自底向上的編程方法也是獲得可重用軟體的最佳途徑。寫可重用軟體的本質在於將共同的部分從細節中分離出來。自底向上的編程過程自然地創造了這種分離。程式設計師不是努力撰寫一個龐大的應用,而是努力創造一個更適合該應用的語言,然後在這個語言上用相對小的努力來撰寫應用。應用相關的特性集中在最上層,而底下的層次可以構成一個適合這類應用的語言——還有什麼比程式語言本身更具可重用性的呢?

論點三:Lisp 支持一種新的軟體開發模式,強調探索、快速迭代和降低錯誤成本。

Common Lisp 不僅讓程式設計師能夠編寫更複雜的程式,而且編寫速度更快。Lisp 程式通常很簡短,因為 Lisp 提供了更高的抽象層次,程式設計師不必編寫大量的低層次程式碼。正如 Frederick Brooks 所指出的,程式編寫所花的時間主要取決於程式的長度,因此 Lisp 程式的簡潔性直接導致更短的開發時間。

這種效率優勢還被 Lisp 的動態特性所放大。在 Lisp 中,編輯-編譯-測試的循環非常短,使得編程感覺像是即時的。這種更高的抽象層次與互動式開發環境相結合,能改變整個組織開發軟體的方式。

書中提到了「快速建型」(Rapid Prototyping)的概念,這種編程方法始於 Lisp。在 Lisp 中,程式設計師可以用比編寫詳細規格說明更短的時間,寫出一個功能性的原型。這個原型通常比用自然語言編寫的規格說明更精確、更具體。而且,Lisp 允許程式設計師從原型順利地轉向產品軟體,因為 Common Lisp 程式,透過現代編譯器的編譯,可以運行得與用其他高階語言編寫的程式一樣快(第 13 章討論速度優化)。

Lisp 支持的開發模式與傳統的「規劃-然後實現」(plan-and-implement)模式形成鮮明對比。傳統模式試圖通過詳細的事前規劃來避免錯誤,假設實現只是將規格轉化為代碼的簡單過程。然而,現實中規格說明由人編寫且難免有缺失,實現過程也充滿挑戰。傳統模式因此往往低效。

Lisp 風格的新模式則假設錯誤是不可避免的,並設法盡量降低錯誤的成本(即修補錯誤所花的時間)。強大的語言和開發環境(如 Lisp 提供的)能大幅降低錯誤成本。這種模式鼓勵更多的探索,更少的事前規劃。規劃被視為一種必要之惡,其程度取決於風險。強大的工具降低了風險,從而減少了規劃的需求。程式設計可以從最有價值的資訊來源——過去實現程式的經驗——中受益。

Lisp 風格的演進證明了這種模式的有效性:少規劃反而意味著更好的設計。通過 Lisp 快速建型,程式設計師可以經歷好幾個設計和實現的循環,而傳統模式可能還停留在規格階段。程式設計師不必過度擔心設計缺失,因為可以更快地發現它們;也不必擔心過多臭蟲,因為函數式風格限制了臭蟲影響範圍,抽象語言使某些臭蟲不可能發生,而剩下的臭蟲由於程式更短且環境互動,更容易找出並即時修補。

這種轉變被類比為文藝復興時期油畫取代蛋彩畫的過程。蛋彩不易修改,使畫家保守;油畫則彈性大,允許「再來一次」,促進了更大膽的作畫方式。Lisp 提供的彈性類似油畫,使得程式設計師可以從事更有野心的專案,正如油畫促成了繪畫的黃金時期一樣。雖然 Lisp 的這種風格尚未完全普及,但其思想(互動環境、垃圾回收、執行期類型等)正逐漸被主流語言借鑒,預示著程式設計領域正經歷一場相似的變革。

總之,本書的核心論點在於將 Common Lisp 定位為一門具有獨特強大能力(閉包、巨集)的語言,這種能力使其能夠自我演化並超越傳統程式範式(如 OOP)。Lisp 的特性集合(動態類型、自動記憶體管理、互動環境)支持一種高效的自底向上程式設計方法,這促進了軟體的可擴充性和可重用性。最終,這些技術基礎共同促成了一種全新的軟體開發模式,強調快速探索、降低錯誤成本,並將程式設計提升到一種更具藝術性和趣味性的層次,這也是 Lisp 黑客精神的核心——「編程應該很有趣,程式應該很優美。」