Brendan Eich#
JavaScript 的創造者,也許是現代 Web 上使用最廣泛、同時也最受爭議的程式語言。Brendan Eich 曾任 Mozilla Corporation 的 CTO,負責 Firefox 瀏覽器的持續開發。
他兼具對優雅理論與務實工程的欣賞,早年在 Silicon Graphics 和 MicroUnity 從事網路與核心程式碼的開發工作。之後加入 Netscape,在極大的時間壓力下發明了 JavaScript。
學習程式設計的歷程#
大學時期:從物理到電腦科學#
- 在 Santa Clara University 就讀物理系,1970 年代末期開始接觸程式設計
- 常跑去 Stanford 使用 DEC TOPS-20 分時系統,學習 macro assembler
- 對 C 語言和 Unix 產生興趣,開始研究 Portable C Compiler 和 yacc
- 對編譯器前端(compiler front end)的形式語言理論特別著迷——parser generator、自動機理論、正規語言理論
- 跳過 Fortran,直接從 Pascal 和 Assembly 開始,後來轉向 C
Eich 強調,從底層組合語言做起的經驗讓他能夠更好地理解各種效能權衡(trade-offs),這是只在高階語言中工作的程式設計師所缺乏的視角。
最早的有趣程式#
- 在一台 DEC 圖形終端機上撰寫 Pac-Man 和 Donkey Kong 的仿製遊戲
- 使用 Pascal 編寫,透過 escape sequences 來輸出圖形
- 這是他第一次認真思考模組化(modularity)和封裝(encapsulation)的程式設計經驗
- 後來開始研究編譯器、撰寫 macro processor 仿製品和 parser generator
從物理轉向電腦科學#
- 物理無法提供暑期工作機會,而大量的 hacking 和實驗室助理經驗讓他在大四轉向 math/computer science
- 被編譯器建構中理論與實踐的結合所吸引
- 數值方法的浮點數問題讓他感到困擾——這後來也影響了 JavaScript(IEEE double 的精度問題)
研究所與業界#
- 前往 University of Illinois Champaign-Urbana 攻讀碩士
- 聽了 Jim Clark 演講後決定去 Silicon Graphics (SGI) 工作
- 1985-1992 年在 SGI 從事核心程式碼(kernel)和網路程式碼(networking code)
- 編寫了封包嗅探器(packet sniffer)的表達式語言和協定描述編譯器
- 1992 年轉至 MicroUnity,做了一些 GCC 和編譯器語言的工作
JavaScript 的誕生#
十天的創造#
- 在 Netscape 時,有人向他推薦了 Abelson 和 Sussman 的著作,原始構想是在瀏覽器中放入 Scheme
- 但 Netscape 管理層要求語法必須看起來像 Java——因為「真正的程式設計」應該用 Java,JavaScript 只是 Java 的「笨小弟」
- 沒有時間直接移植 Scheme 核心,所以他直接從頭開始寫,這也意味著可能重蹈他人的錯誤
Eich 指出 JavaScript 的誕生是在極大的時間壓力下完成的。他沒有時間仔細思考每個設計決策的後果,這導致了一些至今仍存在的語言問題,例如全域物件(global object)作為作用域鏈的一部分。
設計影響#
- Self 語言影響最大——Dave Ungar 的 prototype-based delegation 啟發了 JavaScript 的原型繼承
- HyperTalk(Apple 的 HyperCard)影響了 DOM 事件處理器的命名方式(onFoo)
- awk 影響了使用
function關鍵字(而非lambda)的決定 - NewtonScript 與 JavaScript 有相似的 scope chain 和 parent link 設計——是趨同演化
- Java 的負面影響:被迫加入 primitive types 與 objects 的區別,但拒絕加入 class 系統
語言設計的遺憾#
- 將全域物件設為 window object,導致無法對自由變數做靜態判斷
- 頂層變數成為可變動的全域屬性(mutable properties),可以被任意修改
- 這些「漏洞」(loopholes)包括:全域物件、
with語句、eval - JavaScript 大多是 lexical scope,但這些例外造成了困擾
對 JavaScript 演進的看法#
ECMAScript 4 (ES4) 的失敗#
- ES4 是一個協作努力,與 Adobe(ActionScript 3)合作
- 基於 Waldemar Horwat 在 1990 年代末期的 JavaScript 2/ECMAScript 四版提案
- ES4 回顧起來「太大了」——我們需要對標準保持務實態度
- 不能只說「你只需要 lambda」(Alonzo Church 已經證明了)就不再新增語言功能
Eich 引用了 Peter Norvig 的觀點:設計模式(Design Patterns)其實反映了程式語言的缺陷。與其崇拜設計模式,不如改善語言本身。他認為語言應該持續演進以解決程式設計師面臨的實際問題。
語言演進的哲學#
- 程式設計就像寫作或音樂——需要持續練習這門技藝
- 語言本身很重要(the tonal system matters):我們應該持續演進程式語言
- JavaScript 因為 Web 的相容性需求可能必須保持穩定,但不應被此困住
- 對 Ruby 的折衷主義(eclecticism)表示欣賞,但認為新語言不應被過度炒作
- 沒有銀彈(no silver bullet),但有更好的語言,我們應該遷移過去
Macro 系統#
- 對 JavaScript 加入 macro 系統持開放態度,但面臨 C 語法(非 S-expression)帶來的 AST 定義困難
- Hygiene(衛生宏)的正確性證明仍是研究中的課題
- Dave Herman 正在研究 hygiene 的 soundness 證明
程式設計師是否需要懂底層?#
對底層知識的看法#
- 優秀的 JavaScript 程式設計師不一定需要了解機器指令的映射方式
- 最好的程式設計師會邊寫邊測試基準(benchmark),寫出緊湊的 JavaScript
- 隨著 JIT 編譯器(如 TraceMonkey)的發展,更多人會開始在底層使用 JavaScript
- 真正重要的是 virtual-machine economics(虛擬機經濟學)
抽象的力量與過度抽象的危險#
- 抽象很強大,但他對 1990 年代的 CORBA、COM、DCOM 等物件導向過度抽象深感厭惡
- 在 SGI,核心程式碼才是「真正的程式設計師」的地盤
- 貼近硬體(staying close to the metal)是保持誠實、避免過度抽象的方式
- 但隨著硬體進步,程式設計師確實可以在更高的抽象層次上運作
靜態分析與型別系統#
Mozilla 的靜態分析實踐#
- 對 C++ 的靜態分析很困難,但已找到有效方法
- 關鍵突破:不需要擔心記憶體問題,只要能建構完整的 control-flow graph 並連接所有虛擬方法
- 可以進行部分求值(partial evaluation)來找出死碼、冗餘測試和缺少的 null 檢查
動態語言與靜態型別的平衡#
- 動態語言之所以流行,是因為人們可以快速建立原型(prototype),保留潛在的型別系統
- JavaScript 的 optional typing 仍是委員會中的爭議話題
- 混合型別系統(hybrid type system)可能會進入未來的 JavaScript 版本
- 用 Curry-Howard correspondence 的觀點:型別即證明,程式即命題
語言應該防止程式設計師犯錯嗎?#
- 面向大眾的語言(如 Java)不應該有過於複雜的泛型系統
- 程式設計是工程——工程的一部分是確保各種安全屬性(safety properties)
- 需要更好的語言來處理並行程式設計——不應使用 synchronized blocks、mutexes 或 spin locks
- Joe Armstrong 的 shared-nothing 方法在瀏覽器實作中越來越常見
學術界與產業界的鴻溝#
問題所在#
- 學術界與產業界之間存在嚴重的脫節(disconnect)
- 學術界追逐 NSF 資助,研究靜態型別系統(ML、Hindley-Milner),與業界完全脫離
- Hindley-Milner 型別推論的錯誤訊息非常難以理解——當統一化(unification)失敗時,通常指向錯誤的程式碼
- 學術界未能有效引導人們走向更好的模型
Mozilla 的嘗試#
- Mozilla 正在嘗試推動研究前沿(move the research needle)
- 與實務導向的學者合作,如 Andreas Gal(TraceMonkey 的共同開發者)
- 試圖在學術研究界所尊重的與業界實踐之間架起橋樑
- 編譯器、VM、debugger、Valgrind 等 profiling 工具——投資不足且不夠吸引人,但有突破空間
對 PhD 的看法#
- 不一定建議所有人去讀 PhD——需要特定技能組合
- 在矽谷的通膨繁榮期,不讀 PhD 是合理的經濟取捨
- 但系統性地、悠閒地研究事物的能力很有吸引力
- 有些人適合做研究,有些人適合快速產品週期
除錯方法#
最難的 Bug#
- 多執行緒 Bug 是最困難的——在 SGI 從事 Unix kernel 工作時遇到
- SGI 從 HP 引進了 symmetric multiprocessing,但只用 C、semaphores 和 spin locks
- 產生了大量的 Bug,一個 race condition 需要現場除錯(在澳大利亞的礦業軟體公司)
- 必須先產生測試案例(本身就很困難),然後在時間壓力下找到修復方法
除錯工具與方法#
- 使用 GDB,特別是 Mac 上的 watch-point 功能
- 主要使用
printf進行二分搜尋式的除錯(bisecting) - 對 Chronomancer 和 Replay 等時間旅行除錯器(time-traveling debugger)印象深刻
- Robert O’Callahan 基於 Valgrind 框架開發的除錯器能記錄每個指令,重建任意時間點的程式狀態
Eich 認為除錯技術被嚴重忽視(sadly underresearched),學術界做的是正式證明,而業界使用的還是 1970 年代的 GDB。這是產業界與學術界之間鴻溝的又一例證。
Fuzz Testing 的價值#
- Mozilla/Firefox 大量使用 fuzz testing(模糊測試)
- 在多個原始語言、深層 rendering pipeline 中找到記憶體安全 Bug
- fuzz testing 的生產力超越幾乎所有其他測試方法
- 也投資於靜態分析,雖然相當深奧
程式設計方法#
設計流程#
- 大量原型開發(prototyping)——先寫高階偽代碼,然後由下而上填充
- 通常能在腦中保持高階結構,直接由下而上開發直到拼合
- 陷入困境時會寫偽代碼(pseudo-code),然後由下而上直到完成
- 盡量不讓程式碼停留太久不測試——要能測試、能執行、能逐步檢查
由上而下與由下而上的結合#
- 同時進行大量由上而下和由下而上的開發,在中間會合
- 在設計層面可能有實體關係、粗略的模組化、幾個演算法
- 思考複雜度——是線性的嗎?是常數的嗎?
關於重寫#
- 認同 Peter Norvig 的觀點:設計模式反映了語言的缺陷
- 不是極簡主義者——反對凍結語言的做法
- Mozilla 的第二次大重寫(mozilla.org)是必要的——需要為新貢獻者開放空間
- 對大規模重寫持懷疑態度——需要罕見的資金、時間和市場條件的配合
- 重寫應該在原型開發階段進行,小規模但可能跨越整個程式碼庫
閱讀程式碼#
Code Review 實踐#
- Code review 是 Mozilla 的強制性預提交步驟(mandatory pre-check-in step)
- 有「super review」機制——當修改涉及多個模組時,需要資深開發者從全域角度審查
- 沒有設計審查(design review),有時會導致延遲的設計回饋
閱讀他人的程式碼#
- 喜歡瀏覽開源世界中其他人的程式碼——server frameworks、Python、Ruby
- 特別喜歡看 Ajax 函式庫——closures、prototypes 和 objects 的巧妙使用
- 閱讀 Perl 4 的原始碼啟發了 JavaScript 的正則表達式實作
閱讀大型程式碼庫的方法#
- 從由上而下開始——如果夠大,function pointers 和控制流會變得不透明
- 有時在 debugger 中驅動程式碼來觀察
- 也尋找由下而上的模式——語言處理器、系統呼叫等可辨識的基本操作
- 真正的理解需要從多個角度(由上而下和由下而上)的完形過程(gestalt process)
- 在 debugger 中逐步執行雖然繁瑣,但與閱讀原始碼一樣重要
Eich 指出,僅閱讀原始碼可能會讓你「說服自己理解了其實不理解的東西」。在 debugger 中實際步進(stepping through)程式碼能驗證你的心智模型是否正確。
對程式設計書籍的看法#
- 喜歡 Brian Kernighan 的書——從小規模的程式碼開始,逐步建構和模組化
- 推薦 Knuth 的 The Art of Computer Programming 第 1-3 卷,特別是 semi-numerical 的部分(double-hashing)
- 對「從書本學習程式設計」持懷疑態度——程式設計更像音樂,需要大量練習
- 電腦科學中有一些缺失——實務性的東西尚未達到土木工程或機械工程的水準
對證明的看法#
- 證明很難,大多數人都很懶——Larry Wall 說得對,懶惰應該是美德
- 偏好自動化而非手動證明
- 寫 assertions 是有用的——在 Mozilla 中,好的 assertions 隨時間累積,對理解不變量有所啟發
- 把 assertions 視為「證明要點」(proof points)是有幫助的
- 但不要求完整的形式證明——很多學術論文中的證明本身就有漏洞
對軟體工程的整體觀點#
程式設計的本質#
- 程式設計是工程,不是純科學
- 持續練習技藝很重要——不只是基於舊設計寫程式碼
- 設計和編碼需要相互回饋
並行程式設計的挑戰#
- Transactional memory 不會解決所有問題——無法將所有並行或平行程式演算法映射上去
- Shared-nothing 方法(如 Joe Armstrong 的 Erlang)在瀏覽器中越來越普遍
- 語言的進步應該包括更好的並行程式設計支援
語言的黃金時代#
- 認為我們正在進入程式語言的第二個黃金時代
- 語言創造的興趣和數量都在增加
- 每一次程式設計的大飛躍(machine code -> assembly -> high-level -> structured -> OOP)都需要約一個人類世代的時間
- 下一次飛躍可能與 mash-ups 相關——隨意組合程式碼片段建立新程式
Eich 認為程式語言是程式設計師最重要的工具之一。語言需要持續演進,不應因為相容性或政治因素而停滯不前。他倡導在理論的嚴謹性和實務的可用性之間取得平衡。