Guy Steele#
Guy Steele 是真正的程式語言通才 (polyglot)。當被問到認真使用過哪些語言時,他列出了:COBOL、Fortran、IBM 1130 assembly、PDP-10 machine language、APL、C、C++、Bliss、GNAL、Common Lisp、Scheme、Maclisp、S-1 Lisp、Lisp、C、Java、JavaScript、Tcl、Haskell、FOCAL、BASIC、TECO、TeX。
他參與了兩種主要 Lisp 方言的創建:Common Lisp 和 Scheme。他在定義 Common Lisp、Fortran、C、ECMAScript 和 Scheme 的標準委員會中服務,並被 Bill Joy 招募協助撰寫 Java 語言的官方規格。他目前在設計 Fortress,一種用於高效能科學計算的新語言。
他的學術經歷包括 Harvard 的 AB 學位和 MIT 的 SM 及 PhD。在 MIT 期間,他與 Gerald Sussman 合作發表了被稱為 “The Lambda Papers” 的一系列論文,其中包含了 Scheme 程式語言的原始定義。他也是 Jargon File 的原始編纂者之一,編輯了書版《The Hacker’s Dictionary》。他在 Emacs 的誕生中扮演了重要角色。
Steele 是 ACM Fellow、美國藝術與科學院院士、美國國家工程院院士。1988 年獲得 ACM Grace Murray Hopper Award,2005 年獲得 Dr. Dobb’s Excellence in Programming Award。
程式設計啟蒙#
小學到高中:從科學到程式#
- 小學時就對科學和數學著迷,喜歡 Irving Adler 的 House of Numbers、Danny Dunn 系列科幻小說
- 關鍵轉折點:九年級時在 Boston Latin School 地下室發現了一台 IBM 1130 迷你電腦
- 朋友給他展示了一個五行的 Fortran 程式,他立刻被迷住了
- 跟數學老師借了幾本書,老師以為夠他忙一個月——實際上感恩節長週末(1968 年)就自學了 Fortran
IBM 1130 上的早期探索#
- 和 Latin School 的朋友們經常造訪 IBM 辦公室,用零用錢訂購技術出版物
- 在書店發現 PL/I 等稀奇語言的書籍
- 1969 年春天開始參加 MIT High School Studies Program——週六早上去 MIT 上群論和電腦程式設計等課程
- 透過這個計畫接觸到 IBM 1130 和 DEC PDP-10
學習語言的速度#
- 1968 年秋天學 Fortran
- 隨後學 IBM 1130 組合語言
- 1969 年春天的 Spring Joint Computer Conference 上,從 IBM 展位要來了示範 APL 用過的整疊列印紙——靠這些紙和一本手冊自學了 APL
- 高中時期就為 IBM 1130 實作了自己的 Lisp
進入 Harvard 和 MIT#
- 被 MIT、Harvard、Princeton 三校錄取,校長打電話說服其父母讓他去 Harvard
- 暑假找打字工作(keypunching),因為未滿 18 歲沒人願意僱用
- 聽說 MIT 的 Bill Martin 在找 Lisp 程式設計師,去考了 Lisp 測驗,兩小時後被錄用
- 在 Harvard 上課的同時,在 MIT 的 Macsyma 專案工作——負責維護 Maclisp 直譯器
Steele 同時在 Harvard 和 MIT 的經歷給了他極為寬廣的教育:「我可以跑來跑去,一邊說『河對岸的教授說了這個』,另一邊說『他胡說八道,你應該這樣想』。這讓我很快獲得了非常寬廣的教育。」
重要啟蒙導師#
數學老師 Ralph Wellings#
- 九年級時與他達成協議:每週四堂數學課可以去電腦室,條件是第五天考試要拿 100 分
- 為了使用電腦的時間,Steele 努力確保數學成績完美
- 隔年的數學老師不願提供同樣的優惠,因為他已經掌握了那一年的數學——老師做了正確的判斷
Bill Martin 和 MIT AI Lab#
- Bill Martin 是僱用他的人,Joel Moses 是 Macsyma 專案的負責人
- JonL White 是重要的導師——之前負責 Maclisp 的直譯器和編譯器
- Steele 接手了直譯器的維護,JonL 專注於編譯器
- MIT AI Lab 的人們也對他影響深遠
閱讀程式碼的方法#
從具體命令開始追蹤#
- 如果只是要了解一個軟體的運作方式,會選一個具體的命令或互動來追蹤
- 例如:如果要了解 Emacs,就從「forward a character」的程式碼開始看
- 先了解資料結構,然後看更複雜的操作如「backwards a character」、「kill a line」
- 逐步向上,直到追蹤過重要部分的程式碼
Desk-checking 與心理執行#
- 傾向在桌上印出程式碼閱讀,做標記和註解
- 然後回到電腦上嘗試修改並追蹤
- 現代程式的問題:從啟動到有趣的事情發生之前,已經有很長的初始化過程
- 比較好的方式是找到主命令迴圈或中央控制流程,從那裡開始追蹤
程式碼的組織方式#
- Pascal 因為是為 one-pass 編譯器設計的,程式碼排列是 bottom-up 的——最好的閱讀方式是倒著讀
- 現代語言更自由,但依賴程式設計師的品味來安排
- IDE 可以幫助交叉引用,但他對 IDE 有一個不滿:在圖形式瀏覽時很難知道你是否已經看過所有部分
- 線性排列至少保證你能走過所有東西
好的程式碼應該講述故事#
- 程式碼不只是做什麼的故事,還有如何組織的故事和運行環境的故事
- 好的程式設計師會花心思傳達這些故事——透過區塊註解、概覽文件、或恰當的變數命名
Steele 讀 Knuth 的 TeX: The Program 的經驗:Knuth 精心安排程式碼使其幾乎可以像小說一樣線性閱讀。「一個程式可以如此有組織、如此有文件、如此有索引,以至於你能快速找到任何東西——這本身就讓人大開眼界。」
軟體設計方法#
早期設計方式#
- 有時畫流程圖——他擁有 IBM 的流程圖模板和專用紙
- 經常列出輸入和期望輸出的範例,寫下預期的終端互動
- 在 Maclisp 專案中,大部分是在已有的大型函數集合上新增功能
介面優先設計#
- 越來越傾向介面導向設計
- 熱愛撰寫方法的輸入、操作和輸出描述——不含程式碼
- 設計介面時要確保不會設計出不可能實現的規格
- 評估介面是否良好時考慮:通用性 (generality) 和正交性 (orthogonality),以及是否符合慣例
離開電腦思考#
- 至今仍然會關掉電腦(或讓它休眠),到另一張桌子前攤開紙張思考
- 電腦的誘惑太大——「你的 email、查看 email」
- 有時用白板工作
Maclisp 的 New I/O 重新設計#
- 一個重要案例:為六個不同的作業系統重新設計 I/O 系統
- 用一週時間帶著所有 Maclisp 的列印稿和六套作業系統手冊退回父母的夏季小屋
- 每天六小時在紙上做設計和程式設計
- 回到 MIT 後花了一個月打字輸入和除錯
Steele 解釋為什麼離線設計:每寫一個函數(如 rename)都需要查閱六個作業系統的規格,花一小時研究後只需寫 30 行程式碼。坐在終端前浪費大部分時間不划算。
Emacs 的誕生#
TECO 到 Emacs 的演進#
- 在 MIT 的 24x80 螢幕上,TECO 有一個顯示模式,21 行顯示 buffer,底部 3 行輸入 TECO 命令
- 接著出現了即時編輯模式:每個字元立刻對應一個 TECO 命令
- 很快出現了五個互不相容的 GUI 介面——每個人都有自己的鍵位綁定偏好
Steele 的角色:標準制定者#
- 他扮演的是「標準制定者」和「社群調解者」的角色
- 拿著筆記本在大樓裡跑來跑去,拜訪每個開發者,嘗試建立共識
- 主要考慮助記性 (mnemonic value)——所以 Meta-C、Meta-L、Meta-U 分別對應 capitalize、lowercase、uppercase
- 開始實作一個巨集壓縮器(基於 Moon 的早期想法),此時 Stallman 過來說「這看起來很有趣」
- Stallman 加入後,因為對 TECO 了如指掌,實作速度是 Steele 的十倍
- Steele 在 Emacs 的實作上只工作了約四到六週,之後 Stallman 接手了 99.999% 的工作
Steele 對 Emacs 的貢獻主要是催化作用:建立鍵位綁定共識、開始巨集壓縮器的實作,然後 Stallman 接手完成了絕大部分工作。
數學與程式設計#
數學概念在平行程式設計中的價值#
- 以加法為例說明三種平行策略:
- 循序:初始化為零,逐一相加——依賴 identity(單位元素)
- 成對歸約:兩兩相加形成樹狀結構——依賴 associativity(結合律)
- 競爭式:多個處理器搶數字往共用暫存器加——依賴 associativity + commutativity(交換律)
- 字串串接是 associative 但不是 commutative——這精確告訴你哪些平行技巧可用、哪些不行
- 程式設計師需要知道這些數學概念,即使不需要用數學家的正式術語
Steele 在設計 Fortress 語言時的核心洞察:如果能在語言中明確表達操作的代數性質(如結合律),編譯器就能自動選擇最佳的平行策略。「我不期望每個應用程式設計師都會停下來想『我剛發明的這個子程式是否具有結合律』,但函式庫程式設計師非常在意。」
語言設計哲學#
“Growing a Language” 演講#
- 在 1998 年 OOPSLA 的演講中提出了他最重要的語言設計思想轉變
- 70 年代的觀念是:發明一種語言,完成設計,然後就完成了
- 但語言變得越來越複雜,不可能一次性設計和實作完成——語言必然會演化
- 這改變了他對語言設計的根本思考方式
語言的成長能力#
- Lisp 因為巨集機制的彈性和社群的社會態度,成長得比較容易
- Scheme 因為社群的「否決文化」(blackball culture),成長過程更加痛苦
- Common Lisp 則採用多數決,人們更願意接受不完美的妥協
- 語言是否能良好成長,取決於早期的技術選擇和社會過程的設計
沒有最好的語言#
- 堅信認為一種語言能解決所有問題是錯誤的——不同應用領域適合不同語言
- 做演算法設計時,樂意在白板上混用 Java、Fortran 和 APL 的片段
- 存在 Huffman 編碼問題:如果讓某些東西很簡潔,其他東西必然變得更冗長
- 語言設計的關鍵問題:「什麼東西要讓它很容易說、很容易做對?」
對各語言的看法#
- C++:Stroustrup 設定了完全向後相容 C 的目標,在該約束下設計得很好,但 C 本身有「腐敗的型別系統」是致命缺陷
- Python:組織方式不錯,不同意 Guido 早期不用垃圾回收的決定,但理解他的取捨
- Haskell:「美麗的語言,我愛 Haskell,但不太用它」
- Perl:不太被吸引
- 最喜歡的語言?「我有三個孩子——你問我哪個是最愛的,他們都很棒,各有不同的技能和個性。」
除錯方法#
多種除錯方式#
- 承認自己「懶惰」——第一反應是丟幾個 print 語句看看
- 但在用 Haskell 做專案時因為純函數式語言不能隨便加 print,被迫轉向100% 的單元測試——「這後來證明是非常好的紀律」
- 這個經歷影響了 Fortress 的設計:內建支援在程式碼旁邊撰寫單元測試和 preconditions/postconditions
Assertions 和不變量#
- 傾向在程式碼的關鍵點加入 assertions
- 在思考程式碼正確性時,經常用不變量 (invariant) 的概念——證明不變量被維持
對形式證明的看法#
- 如果程式碼涉及棘手的數學不變量(如排序演算法),會嘗試構建正確性證明
- 證明也可能有 bug——但因為使用不同的工具和思考模式,能增加發現錯誤的機會
- 證明和程式碼往往不是統計獨立的(因為證明通常匹配程式碼結構),但帶來不同的思考角度仍然有價值
Steele 對形式證明的精彩類比:「你使用證明的原因和使用資料型別的原因一樣,和登山者使用繩索的原因一樣。如果一切順利,你不需要它們。但它們增加了在出錯時抓住問題的機會。」
著名的 Bug 故事#
IBM 1130 上的夢中除錯#
- 高中時期在 IBM 1130 上寫反編譯器
- 被一個 bug 困擾了好幾天,半夜突然坐起來,意識到問題所在
- 原因是忽略了介面規格中一個不起眼的腳註——關於卡片讀取器 buffer 中的 low-order bits
Maclisp Bignum 除法 Bug#
- Bill Gosper 報告說兩個約一百位數的大數相除的結果不對(因為商應該接近 pi 的十進位倍數)
- 除法程式基於 Knuth 的演算法,已使用多年,被認為已充分除錯
- Steele 在 Knuth 的書中注意到一個註釋:「這個步驟很少發生,大約每 2 到字長次就出現一次」
- 推理:如果被認為已除錯的程式碼有 bug,最可能在很少執行的路徑中
- 果然在那段「罕見路徑」的程式碼中發現了資料結構未正確複製的副作用 bug
- 修好後 Gosper 很滿意——但一週後又報告更大的數也出錯,在同一段程式碼中找到了第二個同類型的 bug
Steele 的教訓:(1) 可能不只一個 bug,第一次就應該查得更仔細;(2) 如果 bug 被認為是罕見的,那麼查看罕見執行路徑可能會有收穫;(3) 有好的演算法文件(如 Knuth 的書)是除錯的巨大幫助。
程式設計的本質#
程式設計是非自然的行為#
- 「程式設計是一種高度非自然的活動,必須仔細學習」
- 人們習慣讓聽眾自動填補溝通中的空白,但程式碼中的小細節可能影響整體結果
- 遞迴是一門很難學的藝術——Chomsky 證明人們天然使用遞迴,但實際上很少超過三層
- 一旦掌握了遞迴,它就成為最強大的程式設計工具之一
寫作與寫程式#
- 英文散文和程式碼的讀者不同:人類讀者 vs 電腦
- 寫程式碼時首先考慮的是正確性和效率
- 如果需要效率,有時會用到誤導人類讀者的技巧——需要用註釋標記
- 變數名稱、程式碼排版等是為人類讀者提供信號
- 寫英文時則持續擔心歧義性——花大量時間精心構造不易被誤解的句子
「如果你的 Haskell 程式通過型別檢查就不會出錯」是個陷阱#
- 可編譯的程式仍然有無數種出錯方式
- 不能放鬆對正確性的警覺
推薦書籍與資源#
- Knuth — The Art of Computer Programming(幾乎從頭到尾讀完,做了很多習題)
- Aho, Hopcroft, Ullman — 演算法書籍(真正學會排序的地方)
- Gerald Weinberg — The Psychology of Computer Programming(至今仍非常可讀)
- Fred Brooks — Mythical Man-Month
- SIGPLAN Notices 和 Communications of the ACM(早期的期刊,有真正的技術內容)
- Martin Gardner 的數學遊戲專欄(在 Scientific American 中)
使用過的程式語言完整列表#
Steele 認真使用過的語言(「認真」意味著使用超過一個月且寫過實質程式碼):
- COBOL — 高中時第一份賺錢的程式設計工作
- Fortran — 第一個學的語言
- IBM 1130 assembly 和 PDP-10 machine language
- APL — 從展覽會的列印紙自學
- C, C++, Bliss, GNAL
- Common Lisp, Scheme, Maclisp, S-1 Lisp, Lisp, C
- Java — 協助撰寫語言規格
- JavaScript, Tcl, Haskell
- FOCAL, BASIC, TECO — 也被視為程式語言
- TeX — 是最早將 Knuth 的 TeX 移植到其他系統的人之一
當被問到最喜歡哪個程式語言時,Steele 回答:“mu”(禪宗的回答,意指這個問題本身不成立)。每種語言都有其適合的應用領域。