Brendan Eich#

JavaScript 的創造者,也許是現代 Web 上使用最廣泛、同時也最受爭議的程式語言。Brendan Eich 曾任 Mozilla Corporation 的 CTO,負責 Firefox 瀏覽器的持續開發。

他兼具對優雅理論與務實工程的欣賞,早年在 Silicon GraphicsMicroUnity 從事網路與核心程式碼的開發工作。之後加入 Netscape,在極大的時間壓力下發明了 JavaScript。


學習程式設計的歷程#

大學時期:從物理到電腦科學#

  • Santa Clara University 就讀物理系,1970 年代末期開始接觸程式設計
  • 常跑去 Stanford 使用 DEC TOPS-20 分時系統,學習 macro assembler
  • C 語言Unix 產生興趣,開始研究 Portable C Compiler 和 yacc
  • 編譯器前端(compiler front end)的形式語言理論特別著迷——parser generator、自動機理論、正規語言理論
  • 跳過 Fortran,直接從 PascalAssembly 開始,後來轉向 C

Eich 強調,從底層組合語言做起的經驗讓他能夠更好地理解各種效能權衡(trade-offs),這是只在高階語言中工作的程式設計師所缺乏的視角。

最早的有趣程式#

  • 在一台 DEC 圖形終端機上撰寫 Pac-ManDonkey 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)
  • ChronomancerReplay 等時間旅行除錯器(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 認為程式語言是程式設計師最重要的工具之一。語言需要持續演進,不應因為相容性或政治因素而停滯不前。他倡導在理論的嚴謹性和實務的可用性之間取得平衡。