Joe Armstrong#
Erlang 程式語言和 Open Telecom Platform (OTP) 的創造者。Erlang 是一個專為建構高可用性、高可靠性系統(如電話交換機)而設計的語言,其特性也使它非常適合撰寫並行軟體。
Armstrong 原本是物理學家,在攻讀物理 PhD 時因為經費用盡而轉向電腦科學。他曾在 Donald Michie(英國人工智慧先驅之一)手下擔任研究助理,後來在 EISCAT 科學協會和瑞典太空公司從事物理相關程式設計,最終加入 Ericsson Computer Science Lab,在那裡發明了 Erlang。
學習程式設計的歷程#
中學時期#
- 1950 年出生,高中最後一年(約 17 歲)學校所在的地方議會有一台大型主機(可能是 IBM)
- 在 coding sheets 上手寫 Fortran 程式,寄出去,一週後打孔卡片回來
- 打孔員經常犯錯,來回可能要一兩次
- 第一個程式送到計算機中心後,Fortran 編譯器在第一個語法錯誤就停止了
- 從此學到了一個教訓:不要一次送一個程式,要把每個子程式都平行開發並同時送出
Armstrong 在最早期就自然而然地學會了「平行開發」的思維——因為批次處理的週轉時間太慢,必須同時開發所有子程式。這個經驗可能在某種程度上影響了他後來對並行程式設計的思考。
大學時期#
- 在 University College of London 物理系就讀
- 大學課程中有一些程式設計,他非常喜歡
- 特別擅長除錯別人的程式——收費標準是「一杯啤酒」(one-beer problem),困難的會升級到 two-beer 或 three-beer problem
- 閱讀別人的程式碼時會想:「為什麼寫得這麼複雜?」然後直接重寫來簡化它
從物理到電腦科學#
- 想攻讀高能物理 PhD,加入了 bubble chamber 實驗室,那裡有一台 Honeywell DDP-516 電腦
- 這是他第一次可以完全自己操作電腦——核心記憶體是由「little old ladies」手工編織的
- 親眼見到「glass TTY」(最早的視覺顯示終端)時,興奮地說「有一天每個人都會有這個」,被電腦管理員說他瘋了
- 導師告訴他:「你不應該讀物理 PhD,你愛的是電腦」——他是對的
- PhD 因為經費用盡而中斷
- 在 Edinburgh 物理圖書館角落發現了 Machine Intelligence 的棕色封面書籍(來自 Edinburgh 的 Department of Machine Intelligence)
- 寫信給 Donald Michie(曾在 Bletchley Park 與 Turing 共事),最終成為他的研究助理
Edinburgh 與 AI#
- 在 Michie 的實驗室接觸了完整的 AI 工具箱
- 成為 British Robotics Association 的創始成員
- 但 James Lighthill 被政府派去調查 Edinburgh 的 AI 研究,結論是「不會產生任何商業價值」
- AI 資金枯竭,大家說「我們玩得很開心,但還是去做別的吧」
回歸物理相關工作#
- 來到瑞典,在 EISCAT 科學協會擔任物理程式設計師
- 接著在瑞典太空公司工作,建構控制瑞典第一顆衛星 Viking 的應用作業系統
- 當時的環境極為簡陋:所有檔案放在同一個目錄、檔名只有十個字母加三個字母副檔名
- 約 1984 年加入 Ericsson Computer Science Lab
對現代開發工具的反思#
簡單系統的紀律#
- 回顧起來,階層式檔案系統、版本控制等現代工具不一定讓你更有生產力
- 當所有檔案都在同一目錄時,你被迫保持紀律
- 當沒有版本控制時,你對自己的工作必須更有條理
- 軟體開發的大部分工作其實發生在你的腦中
選擇的痛苦#
- 現在的年輕程式設計師面臨 20 種程式語言、數十種框架和作業系統的選擇——被選擇所癱瘓(paralysis of choice)
- 以前沒有這種選擇的痛苦——你只有 Fortran 和一個編譯器,直接開始做就是了
Armstrong 的觀點相當獨特:他認為現代工具的便利可能反而減少了程式設計師的紀律性。當限制更多時,你反而被迫做更好的設計。
打開黑箱的哲學#
軟體重用的問題#
- 軟體重用(software reuse)的效果「糟糕透頂」(appallingly bad)
- 如果黑箱無法正常工作又無法修改,不如從頭開始自己寫
- OTP 框架在某種程度上是可重用的,但如果它不完全符合你的需求,你就有問題了
打開抽象邊界#
- 曾經犯的通用錯誤:「這個黑箱太難懂了,我不會打開它」
- 但當你真的打開時,往往發現比想像中簡單
- X Windows 的例子:本質上就是一個 socket 加上一個 protocol。X protocol 有大約 100 個訊息,你只需要大約 20 個就能做有用的事。直接對 socket 發送訊息,完全不需要 callback libraries
- PostScript 的例子:打開抽象邊界後發現它就是一個程式語言,學起來很容易
Armstrong 認為物件導向語言缺乏重用性的原因是:它們攜帶太多隱含的環境。「你想要一根香蕉,但你得到的是一隻拿著香蕉的大猩猩,以及整座叢林。」而函數式語言的 pure functions 因為沒有隱藏狀態,本質上就是可重用的。
連接程式的簡單方式#
- Unix pipe 機制(
A | B | C)是簡單連接程式的典範 - 但程式設計師卻使用 API 和共享記憶體來連接程式——這是非常複雜的方式
- 程式設計與實體世界建構方式根本不同:硬體用訊息傳遞(message passing),晶片之間並行執行、發送訊息
- 軟體應該像硬體一樣建構——Dataflow programming 是宣告式的,沒有循序狀態的概念,非常容易理解
Erlang 的誕生#
從 Prolog 開始#
- 最初只是 Prolog 上的一些修改,Robert Virding 加入後開始認真協作
- 在 Ericsson 內部找到需要新程式語言或更好的電話系統程式設計方式的人
- 每週與真實使用者會面一次,六到九個月——教他們如何程式設計,他們教 Erlang 團隊電話系統的問題
- 使用者研究後發現效能需要提升 70 倍
- Armstrong 用 Prolog 寫了編譯器,Mike Williams 用 C 寫了虛擬機
- 編譯器能夠自舉(bootstrap)——從 Prolog 根源脫離,成為獨立的語言
Erlang 的設計影響#
- 直接來自 Prolog:unification / pattern matching、資料結構(tuples 和 lists)
- Tony Hoare 的 CSP(Communicating Sequential Processes)
- Dijkstra 的 guarded commands——要求每個分支的 pattern 都必須匹配,不應有 default case
- 函數式特性是從 Prolog 加入並行後自然演化而來的:一旦加入並行,就不能在操作後 backtrack 來撤銷效果
Erlang 的限制#
- 完全抽象掉記憶體——JPEG 影像轉 bitmap 這類精確記憶體操作不適合
- 依賴破壞性狀態更新(destructively upgrading state)的演算法表現不佳
- 解決方法:用 C 或 assembler 寫這些部分,或用 Erlang 方言交叉編譯成 C
程式設計方法的演變#
思考的轉變#
- 年輕時會寫程式直到完成,完成後停工,然後過一段時間得到新洞察:「啊!錯了!」——然後重寫
- 曾想:「如果能在不寫程式碼的情況下就得到這些洞察,那有多好?」
- 現在他能做到了——花了約 20 年學會如何程式設計,現在不需要做那些實驗了
直覺的角色#
- 年輕時會熬夜寫程式到凌晨四點——「macho programming」
- 學到的教訓:疲倦時寫的東西第二天都要扔掉
- 真正好的程式碼是在心流狀態(flow state)下寫出的——完全不知道時間流逝,放鬆地坐著打字,看著程式碼出現在螢幕上
- 當直覺說「不對」時——停下來,別寫程式碼,做別的事
Armstrong 在心理測驗中的直覺分數非常高,但邏輯思維分數出乎意料地低。他曾以為自己是邏輯型的人(因為數學好),但現在認為程式設計中有大量的直覺——「就是知道什麼是對的」。
設計流程#
- 花大量時間思考和塗鴉——畫泡泡、箭頭、方程式、符號
- 向同事解釋問題的過程中常常自己找到答案——就像對一隻熊解釋你的問題(rubber duck debugging 的前身)
- 比喻為米開朗基羅畫西斯汀教堂天花板:先畫出大幅的草圖,然後逐步填充細節,困難的部分需要集中注意力
由下而上 + 測試驅動#
- 從 bottom-up 開始:寫一點、測一點
- 已經轉向 test-first:先寫測試案例,再寫程式碼
與 Robert Virding 的「串列式配對程式設計」#
交替重寫模式#
- 與 Robert Virding 會輪流工作兩到三週,然後把程式碼交給對方
- 每次交接後,對方都會做出大量修改——幾乎認不出來
- 但每個循環都讓程式碼變得更好
哲學差異#
- Armstrong 信奉 Unix 哲學:程式應該做它該做的事,而且只做那件事——每次接手都會讓程式更短、更具體
- Virding 的哲學:程式應該是一般性的,程式本身是一般程式的特殊案例——會增加通用性,讓程式更長
- 這兩種極端之間的振盪讓結果比任何一個人單獨做的都好
OTP 的設計流程#
- 由 Armstrong、Martin Bjorklund 和 Magnus Froberg 三人設計
- 每天早上喝咖啡時進行 1-2 小時的長時間討論,把白板填滿
- Armstrong 負責寫所有文件,他們負責寫所有程式碼
- 寫文件的過程會發現問題:「我無法描述這個,我們必須改設計」
- 一天一輪:約兩小時討論 + 約兩小時寫文件/程式碼
- 這個流程持續十到十二週,建立了基礎架構
先解決困難的問題#
- 設計系統時,先解決困難的問題,容易的問題自然會水到渠成
- 原型和最終版本要分清楚——原型可以簡單(例如設定檔就是直接讀取的檔案),最終版本再做完整(例如用 XML 和完整文法)
除錯方法#
Print Statements#
- 主要使用 print statements(printf 除錯法)
- 「偉大的程式設計之神說過:在你認為程式出錯的地方放 printf 語句,重新編譯,然後執行。」
Joe 的除錯定律#
- Joe’s Law of Debugging:所有的錯誤都在你上次修改的程式中前後三行之內
- 如同修車——出問題的總是你最後改動的那個東西
區間減半法#
- 在程式的某個點印出變數,確認是否正確
- 如果正確,往後找;如果錯誤,往前找——二分搜尋式除錯(interval halving)
- 只要錯誤可重現,就一定找得到
- 對非可重現的錯誤(如 real-time 系統、garbage collector)就比較困難
早期 Erlang 的除錯故事#
- 早期 Erlang crash 了——只打了約 12 個字元就 crash
- 透過記住精確的輸入序列,花了一個半小時嘗試約一百種不同的輸入組合
- 最終再次觸發了 garbage collection error,然後就能除錯了
對文件的看法#
文件即思考#
- 認為程式在寫完合理的文件之前不算完成
- 對於非常困難的問題,會先從寫文件開始
- 說「讀程式碼就好」是不專業的——程式碼告訴你它做了什麼,但不告訴你它應該做什麼
- 如果沒有文件或規格,你必須從答案猜測問題——你可能會猜錯
Literate Programming#
- 曾用 literate Erlang 做過一些東西
- 寫文件和寫程式碼使用不同的思維模式——文件讓你不同地思考程式
- 在 Haskell 中會被迫在早期思考型別並記錄下來;在 Lisp 或 Erlang 中可以直接開始寫程式碼而不考慮型別
- 寫文件在某種程度上就是在做型別分析:「一個 melody 是一系列的 notes。一個 note 是…」
對程式語言的看法#
喜歡小而簡單的語言#
- 不喜歡 C++——「太複雜了,感覺不對」
- 喜歡小而簡單的語言
- 用過很多語言:JavaScript、Tcl、C、Prolog、Erlang、Fortran,一點 Ruby 和 Haskell
- 學習做不同事情的語言才有意義——學很多做同樣事情的語言沒有意義
Prolog 的獨特性#
- Prolog 是與所有其他程式語言都完全不同的思維方式
- 用 Prolog 寫第一個程式時非常震撼:「你只是告訴它關於你的系統、關於問題的一些事實,然後它就在幫你想辦法了」
- Prolog 適用於大量問題集合,可惜不被廣泛使用
- Kowalski 稱它為「尋找問題的解決方案」
函數式程式設計的核心#
- 函數式程式設計的核心是不可變狀態(nonmutable state):x 不是記憶體位置的名稱,而是一個值
- 這對理解程式、平行化程式和除錯程式都有巨大好處
- 希望能在 Erlang 中加入靜態型別系統的好處
- 動態型別在 marshaling 資料結構時有優勢——可以把任意程式透過網路傳送並在另一端重建
團隊工作與協作#
獨自工作與團隊的平衡#
- 喜歡在團隊中工作但獨自程式設計
- 咖啡休息時的對話非常有價值——走路去上班時的想法在討論中會湧出
- 向同事解釋問題的過程中,把問題從大腦已解決的部分轉移到能夠語言化的部分——這個過程本身就會讓你看到答案
配對程式設計的看法#
- 當兩人都在探索未知領域時,配對程式設計很有幫助
- 如果能力差距太大,弱的一方只會覺得自己很蠢
- 有些特別困難的問題需要完全集中的狀態(complete concentrated state),不希望被打斷——此時配對程式設計會很有干擾性
對程式碼之美的看法#
程式碼如雕塑#
- 如果給兩個程式設計師同一個較數學性的問題,他們通常會寫出同構的(isomorphic)程式碼——完全相同的演算法
- 這引出一個問題:我們是在創造演算法,還是在揭示一直存在的結構?就像拉去雕像上蜘蛛網的感覺
- 喜歡極簡主義的、優美結構的程式碼——像風水一樣
- 如果你持續移除東西,到了再移除任何東西就無法運作的程度——那就是美
程式設計是理解#
- 程式設計不是把程式碼打進機器——程式設計是理解事物
- 為什麼會想實作 JPEG?因為想理解 wavelet transforms
- 為什麼做 X Windows 介面?因為想理解 X protocol 如何運作
- 想理解 C?寫一個 C 編譯器。想理解 Lisp?寫一個 Lisp 直譯器
- 寫編譯器其實不難——有很多小東西要學(hash tables、parsing、code generation、interpretation techniques),每一個都不難
持續學習#
Hamming 的建議#
- 引用 Richard Hamming 給年輕研究者的建議:「做好東西,在好的領域,其他都不重要」
- Hamming 說花 20% 的時間學新東西——複利效應意味著四年半後你會比同事知道兩倍多的東西
- Armstrong 自己花 20-40% 的時間思考新東西,已經持續 30 年
Armstrong 的建議非常明確:花 20% 的時間學習新東西——因為複利的力量,這會在長期產生巨大的差異。「真正優秀的程式設計師花大量時間程式設計。我沒有見過不花大量時間程式設計的優秀程式設計師。」
寫作的重要性#
- 寫作是程式設計之外最重要的技能
- 有電腦科學家說:「如果你英文不好,你永遠不會是很好的程式設計師」
- 建議大學培養學生「能夠清楚地寫作和有條理地論證」——但大多數電腦科學畢業生的寫作不是強項
對軟體工程的整體觀點#
程式設計是美學追求#
- 程式設計涉及工藝(craftsmanship)、數學、人際技能和散文能力
- 這些我們不一定認為是工程的東西,卻是成為優秀工程師的必要條件
- 這是地球上最有趣的工作之一
- 「我們非常幸運,在這些技能能夠通往這些工作的時代成長」
智力是向量而非純量#
- 智力不是一個數字——它是向量(vector quantity)
- 如果你缺乏同理心或情緒智商,就不應該設計 API 或 GUI 或語言
- 程式設計世界中常見的問題:最聰明的人認為他們應該做所有決定,但不同類型的智力適合不同類型的工作