Paul Graham:on Lisp@1993

《On Lisp》核心主旨闡釋

《On Lisp》一書,由 Paul Graham 所著,其核心論點圍繞著 Lisp 語言獨特的「可程式化」(programmable)本質,以及如何利用這種本質來實踐一種被作者視為更優越的程式設計方法:「由下而上」(bottom-up)設計。本書旨在教導讀者如何成為更優秀的 Lisp 程式設計師,其關鍵在於學習如何根據具體程式的需求去彎曲和擴展 Lisp 語言本身,而非僅僅在現有語言的框架下編寫程式。

Lisp 的可程式化本質

首先,作者強調 Lisp 的核心特徵在於其前所未有的可擴展性。不同於大多數程式語言將內建操作符與用戶定義函數區分開來,Lisp 採取了統一的模型:函數應用。Lisp 的核心功能大部分本身就是用 Lisp 函數實現的。這意味著程式設計師可以定義自己的函數和操作符,使其行為與語言內建的無異,有效擴展了語言本身的功能集。

更為關鍵的是,Lisp 程式碼可以表示為 Lisp 的核心數據結構——列表(lists)。這種「程式碼即數據」(code is data)的特性,為 Lisp 程式提供了對自身程式碼進行操作和轉換的強大能力。程式設計師可以編寫程式來生成、分析或修改其他 Lisp 程式碼。這種元程式設計(metaprogramming)能力,特別是透過宏(macros)來實現的程式碼轉換,是區分 Lisp 初學者和專家程式設計師的關鍵所在,也是由下而下設計風格的基石。宏允許程式設計師在程式碼被編譯或解釋之前,根據需要重塑其語法和結構。

由下而上的設計哲學

基於 Lisp 的這種可程式化本質,《On Lisp》提出並詳細闡述了「由下而上」的程式設計方法論。傳統的「由上而下」設計傾向於將複雜問題分解為子問題,逐步細化實現。而由下而上設計則是在解決問題的過程中,同步構建一套專為該問題領域設計的更高層次的抽象語言或工具集。程式設計師不僅是將程式碼「向下」寫入 Lisp,更是將語言「向上」構建來適應程式。

這種方法不是對由上而下設計的完全替代,而是重要的補充。在面對規格複雜或開放性的現代軟體專案時,單純的由上而下方法可能力有未逮。由下而上設計允許程式設計師在開發過程中不斷探索和完善程式碼所使用的語言,使語言與程式碼彼此契合。當程式碼的某一部分顯得重複或冗長時,這被視為一個訊號,表明需要定義一個新的函數或宏來抽象這種模式,從而擴展語言,使程式碼更為簡潔。

程式碼與其依賴的語言共同演化,邊界被不斷重新劃定,最終形成一個高度協調、彷彿語言為程式量身打造的系統。

由下而上設計的顯著優勢

作者主張,這種由下而上的設計風格能帶來諸多顯著的優勢,從而產生更優質的軟體:

  1. 程式碼的精簡與靈活性: 通過將通用邏輯抽象到語言層,應用程式本身的程式碼量會大大減少。更短的程式碼意味著更少的組件、更少的組件間依賴,從而降低了複雜性,使程式更容易理解、修改和維護。這類比於設計零件更少的機器,減少了出錯的機會。
  2. 促進程式碼重用: 在由下而上設計過程中創建的通用工具集(稱為 utilities 或 libraries),可以在不同的專案中重複使用。隨著程式設計師積累的工具越多,開發新程式的效率和速度也會指數級提升,因為許多基礎工作已被抽象和自動化。
  3. 提升程式碼可讀性: 雖然初次接觸由下而上程式碼的讀者可能需要學習新定義的抽象操作符,但這通常比理解大量重複或低層次的程式碼更容易。程式碼變得更具表達力,以更接近問題領域概念的方式呈現,提高了抽象層次。
  4. 改進程式設計過程: 由下而上設計鼓勵程式設計師持續反思程式碼中的模式和結構,這是一種主動的、探索性的過程,有助於澄清程式設計思路,甚至可能發現意想不到的、更優雅的整體架構。結合 Lisp 互動式環境的快速回饋,程式設計師可以即時測試新定義的操作符,這種快速迭代的能力使得在開發過程中進行設計決策變得更加可行和高效。程式設計不再只是嚴格遵循預定計劃的執行,而更像是一種演進和發現的過程。

宏與嵌入式語言:由下而上的關鍵工具

實現由下而上設計,Lisp 的函數(特別是作為第一類對象)和宏是不可或缺的工具。函數作為第一類對象,使得創建接受或返回函數的抽象(如映射、過濾、函數組合等)變得自然和高效。

而宏,作為「編寫程式的程式」,則提供了更深層次的可程式化能力。它們在程式碼被編譯前的轉換階段運作,允許程式設計師創建自定義的控制結構、語法結構,甚至整個領域特定語言(domain-specific languages, DSLs)。宏的使用是 Lisp 中由下而上設計最典型的體現,也是本書著墨最多之處。掌握宏不僅是技術上的進步,更是思維模式的轉變,將程式碼視為一種可以任意操作的數據。

透過宏,程式設計師甚至可以在 Lisp 中實現「嵌入式語言」(embedded languages)。這意味著可以構建一個新的語言,其語法和行為可能與 Lisp 大相逕庭,但底層實現是透過宏將新語言的表達式轉換為 Lisp 程式碼。這種方法利用了 Lisp 已有的解析器和編譯器基礎,通常比從零開始編寫編譯器或解釋器更為省力,同時能夠生成高效能的程式碼。本書的後半部分透過構建查詢語言、處理程式、非確定性選擇機制,甚至實現一個精煉的 Prolog 系統,來展示嵌入式語言的強大潛力,作為由下而上設計的終極範例。

結論:「何時選擇 Lisp?」

總而言之,《On Lisp》的核心論點是,Lisp 憑藉其獨特的可擴展性(特別是程式碼即數據和強大的宏系統),天然地適應並鼓勵程式設計師採用由下而上的設計風格。這種風格通過構建與程式碼共同演化的領域特定抽象,產生更精煉、更具重用性、更易讀且更高效的程式。這種方法在面對現代軟體的複雜性和對可擴展性的需求時尤其有效。作者認為,Lisp 的力量來源於其一系列相互協作的特性,隨著硬體和軟體技術的進步,Lisp 相對於其他語言的優勢日益凸顯,其價值已不再是「為何選擇 Lisp?」,而是「何時選擇 Lisp?」。本書提供了實踐這種程式設計風格的技巧和範例,旨在幫助讀者釋放 Lisp 的全部潛能。