跳轉到

7.2 同像性(Homoiconicity):代碼即資料、資料即代碼的極致對稱#

一個定義與一個深淵#

同像性:語言的程式表示與資料表示是同一種結構。在 Lisp 中:

(+ 1 2)          ; 這是一個算式——執行它得到 3
'(+ 1 2)         ; 加引號——它是一個「列表」,是資料
(eval '(+ 1 2))  ; 把資料當程式執行——又得到 3

quote(')與 eval 之間的往返,就是「代碼 ⇄ 資料」的雙向門。這正是哥德爾編號與圖靈通用機的思想在語言層面的復刻:程式是資料,資料可以是程式。本書從第 2 章到這裡的線索,在此閉合成一個日常工具。

巨集(Macro):在編譯期寫「寫程式的程式」#

同像性最強大的兌現是巨集:接收代碼(列表)、返回代碼(列表)的函數,在編譯期執行。

(defmacro unless (condition body)
  `(if (not ,condition) ,body nil))

用戶可以用語言自身擴充語言——新增控制結構、領域特定語法(DSL),如同 Lisp 的語法本身「不存在」、一切都是庫。對比:C 的預處理器宏只是文本替換(無資料結構、易出錯),Lisp 宏操作的是真正的語法樹——元程式設計的正確形態。

現代繼承者:

  • Rust 的 macro_rules! 與過程宏(serde 自動生成序列化代碼)
  • Julia(2012):宏 + 多重派發,科學計算的「Lisp 血統復興」
  • Elixir:建立在 Erlang VM 上的 Lisp 式宏語言
  • Clojure(2007):Lisp 重返 JVM 與雲端——同像性在分散式資料處理中找到了新舞台

同像性的兩面#

優勢 代價
語言可無限擴充(DSL、新語法) 風格碎片化——每個專案的「方言」不同
編譯期計算(零執行期成本) 錯誤訊息更難定位(展開後的代碼)
代碼轉換、生成、分析的統一模型 工具鏈(IDE、補全、重構)難以支援任意宏
教育價值:語言不再是黑盒 工業界的可維護性焦慮

「語言是可塑材料」與「語言是團隊契約」的張力(7.1 結尾)在同像性上達到極端。大型團隊最終往往自縛手腳(限制宏的使用),而小團隊與研究者則把它當超能力——抽象能力的分配,本身就是組織政治。

歷史支流:同像性思想的其他化身#

  1. Smalltalk:反射 API(物件在執行期查詢與修改自身結構)——同像性的物件導向版本
  2. Forth(1970):字典驅動、代碼即詞彙表——嵌入式世界的極簡同像
  3. JavaScript 的 eval:弱化版(JS 選了「函數是一等公民」而非「代碼是列表」)
  4. SQL 動態拼接 → ORM → 查詢構建器:資料領域的受限同像
  5. LLM 時代:prompt 中嵌入代碼、代碼中生成 prompt——自然語言與程式碼的互指,是同像性的第三形態(第 16 章展開)

文化迴響:「Lisp 的詛咒」#

1980s 有句名言:「Lisp 程式員知道一切語言的價值,並拒絕使用它們。」背後是 Lisp 社群的特殊文化:

  • MIT AI 實驗室的駭客倫理(Levy 的《駭客》1984 記錄的「資訊應當自由」)從這裡發源,流向自由軟體運動(Richard Stallman 的 Emacs 與 GNU,1983——駭客文化→開源運動的制度化)
  • SICP(1985)把「計算 = 組合、抽象、解釋」變成一代人的世界觀
  • Lisp 機器商業崩潰(1987–90)與 AI 寒冬,讓 Lisp 學界離散——學問以「特性」的形式散落進了 Java、Python、JavaScript、Clojure

「語言死了,思想永生」——這是程式語言史最反覆出現的悼詞,Lisp 是它的第一位主角。

小結#

概念 內容 深層對應
同像性 代碼=資料(S 表示式) 哥德爾編號的日常化
quote/eval 資料⇄程式的雙向門 通用圖靈機的語言級實現
巨集 代碼轉換代碼 元程式設計的正確形態
DSL 用語言擴語言 領域知識的語法化

下一節:7.3 垃圾回收與遞歸