LISP — 語言 67 年前 至今仍活在你所撰寫的每種語言中

Back
Category : News

你每天撰寫 if-else
你在終端機執行程式碼然後 REPL 回應你
你在 Python 使用 lambda、在 JavaScript 使用 arrow function、在 Rust 使用 closure

— 這一切 都源自 LISP 語言

而最令人驚訝的是… LISP 從一開始就從未被 planned 成為一門程式語言



改變世界的單一紙頁

1958 — John McCarthy 在 MIT 開始發展 LISP 的概念
1960 年 4 月 — 32 歲的 McCarthy 在 Communications of the ACM (vol. 3, 頁 184-195) 發表論文

“Recursive Functions of Symbolic Expressions and Their Computation by Machine, Part I”

在這篇 12 頁的論文中,McCarthy 提出了程式語言的理念,該語言:

  • 處理符號 (symbols) 而非僅處理數字
  • 使用遞迴作為控制流程,而非使用 loop
  • 將 function 視為 first-class citizen — 可以像資料一樣傳遞

McCarthy 將其寫成 數學理論 — 並無打算實作

但在論文發表前 — 1958-59 年間,研究生 Steve Russell 閱讀了手稿

“I told him, ‘Steve, why don’t you program this eval?’ and he said to me, ‘Oh, I misread what you meant. I thought you meant I should implement the interpreter.'”

— John McCarthy, ACM interview

這位學生 為老師的理論撰寫了解釋器 — LISP 語言就此誕生

Russell 撰寫的第一版程式碼只花了 2-3 天(完整的 LISP 1.5 Programmer’s Manual 則在之後的 1962 年才出版)



LISP 所創造的事物(以及我們每天都在使用它們)



1. if-then-else

回到 1958 年 — 大多數語言只有 GOTO 與類似 assembly 的 branch

McCarthy 創造了 cond(條件表達式)— 這就是我們今天在幾乎所有語言中看到的 if-else 之起源

(cond ((< x 0) 'negative)
      ((= x 0) 'zero)
      (t 'positive))

Enter fullscreen mode

Exit fullscreen mode

C、Java、Python、JavaScript、Go、Rust — 這些語言全都繼承了這份遺產。所有現代語言的條件分支,其結構都與 McCarthy 在人類登月前所構思的一樣



2. Garbage Collection

在 LISP 之前 — 程式設計師必須 100% 自己管理記憶體,每一行都要處理 mallocfree

LISP 創造了世界上第一個垃圾回收機制 — 其原始版本是 mark-and-sweep 演算法(由 MIT 學生 Daniel Edwards 實作)

如今 GC 已成為幾乎所有高階語言的預設機制 — Java、Python、JavaScript、Go、C#、Ruby 都延續並發展了這個概念



3. Lambda — 函式即資料

(lambda (x) (* x x))

Enter fullscreen mode

Exit fullscreen mode

LISP 讓函式成為 first-class citizen — 可以將函式當作參數傳遞、回傳函式、將函式存入變數,就像處理字串或整數一樣

這就是以下特性的源頭:

  • JavaScript — arrow function (x) => x * x(Brendan Eich 原本被 Netscape 聘請是要在瀏覽器中實作 Scheme — 但管理階層改變主意,要求語法像 Java)
  • Pythonlambda x: x * x
  • Ruby — blocks、procs、lambdas(Matz 曾說 “Ruby was a Lisp originally, in theory”)
  • Java — lambda expressions(Java 8,2014 年 — 花了 19 年才追上)
  • C++ — lambda(C++11)



4. REPL — 「與語言對話」的哲學

Read-Eval-Print Loop — LISP 在 1960 年代創造了它

在那之前:撰寫程式碼 → 編譯 → 執行 → 除錯 → 重複
在那之後:輸入表達式 → 按下 enter → 立即看到結果

如今當你開啟 Python REPL(>>>)、Node.js console、Ruby IRB、Chrome DevTools、Rust Playground 或 Elixir IEx — 你正坐在與 60 年前 LISP 程式設計師相同的教室裡



5. Homoiconicity — 程式碼 = 資料

'(+ 1 2)          ; ← 這是一個 list
(eval '(+ 1 2))   ; ← 這是執行該 list 的程式碼

Enter fullscreen mode

Exit fullscreen mode

LISP 是用… LISP 本身撰寫的 — 程式碼與資料使用相同的結構(S-expression)

這代表 程式可以修改自己 — 無需使用 parser 來分離 AST,也無需撰寫 transformer

這是程式語言中最強大的 macro system 之基礎

沒有任何語言能像 LISP 一樣完整實現 — 但「code as data」的概念出現在:

  • Elixir — macro(接收 AST,回傳 AST)
  • Julia — macro + multiple dispatch(”We want a language that’s homoiconic, with true macros like Lisp” — Julia manifesto, 2012)
  • Rust — macro system(proc macro)
  • Clojure — macro(Clojure 本質上就是 LISP 的方言)



來自 LISP 的遺產

LISP (1958)
├── Scheme (1975) — minimalist, lexical scoping
│   ├── JavaScript (1995) — Brendan Eich 原本打算在瀏覽器中實作類似 Scheme 的語言
│   │   └── arrow functions, closure, first-class functions
│   └── Racket (1995) — 用於教學與研究的語言
├── Common Lisp (1984) — 工業級、實用導向
│   └── Emacs Lisp (1985) — 編輯器腳本語言 (GNU Emacs)
├── Clojure (2007) — 跑在 JVM 上的 LISP,預設不可變
│   └── 點燃了企業界的功能式程式設計熱潮
└── Python, Ruby, Elixir, Julia, Rust, Swift — 每種語言都採用並調整了 LISP 的概念

Enter fullscreen mode

Exit fullscreen mode



為什麼 LISP 今天不是主流語言

儘管它創造了我們幾乎所有正在使用的創新 — 為什麼 LISP 沒有勝出?

Paul Graham(Y Combinator 創辦人、LISP 鐵粉)在散文 “Beating the Averages” 中解釋道:

  1. 語法與其他語言完全不同 — 滿滿的括號讓人卻步
  2. 早期缺乏標準函式庫 — Common Lisp 在 1984 年才挽救,但已經太晚
  3. AI Winter — LISP 與 AI 研究緊密結合;當 AI 經費消失,LISP 也隨之消失
  4. 工具鏈 — 編譯器慢、IDE 差(與 90 年代的 Visual Studio 相比)

Graham 堅稱:

“Lisp is a language that was discovered, not invented.”

而 LISP 的批評者(包括曾經在生產環境使用後轉向其他語言的人)指出了 Graham 未提及的額外問題:

  1. 大型系統中的動態型別 — 重構困難、編譯器能捕捉的錯誤較少、在數十萬行程式碼的專案中除錯耗時
  2. 直到 2000 年代的效能 — 在 SBCL 和 Chez Scheme 出現之前,LISP runtime 在當時的硬體上遠比 C/C++ 慢,尤其在數值運算方面
  3. 碎片化 — Common Lisp 與 Scheme 從未達成共識;社群四分五裂而非團結力量

總結:沒有單一原因 — 這是語法差異 + 生錯時代 + 社群分裂 + 缺乏企業贊助(與 Sun 全力支持 Java、Microsoft 全力支持 C# 形成對比)的 完美風暴



看不見的遺產

這篇文章並非要告訴你「你應該去寫 LISP」

但每當你:

numbers = [1, 2, 3]
squared = list(map(lambda x: x * x, numbers))

Enter fullscreen mode

Exit fullscreen mode

const result = data
  .filter(x => x.active)
  .map(x => x.value);

Enter fullscreen mode

Exit fullscreen mode

let squared: Vec<_> = numbers.iter().map(|x| x * x).collect();

Enter fullscreen mode

Exit fullscreen mode

— 你其實正在不知不覺中撰寫 LISP




延伸閱讀


📅 2026 年 8 月 | ⚠️ 資料以撰寫當時為準

https://dev.to/gophernment/lisp-phaasaa-67-piikn-thiiyangmiichiiwityuuainthukphaasaathiikhunekhiiyn-1cfe

https://www.worldprogramming.org/posts/lisp-67-7mxpbu