獲得圖靈獎是一項巨大的榮譽。而它為我所做的一件事是讓我反思我所做的工作,以及我如何產生那些讓我獲得獎項的想法。這就是我今天要談論的內容。首先,我將談論我發明資料抽象想法時的情況,以及我是如何想到這個想法的。然後我會稍微談談 CLU 語言,這是我為了探索這個想法而開發的語言。接著我會稍微談談類型層次結構,最後以一些關於接下來可能發生的事情的評論作結。

早期工作:Venus 機械與作業系統

好的,我在 Stanford 獲得了我的博士學位,我實際上是在人工智慧領域完成了我的論文。我做了一篇關於如何下西洋棋殘局的論文。我在研究生生涯中途意識到我真的很想轉向系統領域。當我完成博士學位後,我去了 MITRE Corporation 工作,我在那裡參與的第一個專案叫做 Venus Machine。而 Venus Machine 是一個硬體架構,實現於 Interdata 3 上。當時的情況是,人們對所謂的微程式設計非常感興趣。而微處理器有點像 RISC 機器。我想,這台機器只有 16 個指令,據我所知。它可能有 16K 的唯讀記憶體。這個想法是利用那個唯讀記憶體和那一小組指令來實現一個更豐富的指令集。現在,今天看來可能有點奇怪,我們對那個微指令集並不完全滿意。我認為我們對它不太滿意的原因是,當然,RISC 的革命還沒有發生,而且當時人們用機器語言編寫了大量的程式碼,用機器語言寫程式碼相當乏味。編譯器當時還不像現在這樣成熟,而高階語言則帶有一定程度的懷疑眼光,因為人們擔心它們產生的機器碼是否足夠高效等等。總之,這個專案是為了看看這種微架構能做什麼,我開發了這台 Venus machine,它有一個更豐富的指令集。這台機器最有趣的事情是它實際上在硬體中有號誌(semaphores)。我讀過 Dijkstra 非常有名的 THE system 論文,我對號誌作為一種同步機制非常感興趣。所以在設計完 Venus machine 之後,當然,接下來的事情就是你會問,這有用嗎?當然,這是一個標準的程式語言問題。你知道,你設計你的程式語言,它有用嗎?所以為了回答這個問題,我設計了 Venus operating system。這是一個小型多程式系統,時間大約是在 1969 年左右。

軟體危機與程式方法學

好的,當時 Multics 正在開發中,還處於早期階段。所以我開發了 Venus operating system,當我完成那項工作後,大概在 1970 年底的某個時候,我就開始研究程式方法學。當時的情況是人們開始關注軟體危機。事實上,我們從未擺脫軟體危機,所以這更像是軟體危機的開始,而不是結束。但人們已經開始意識到我們並不擅長構建軟體,而且我們想要實現的目標遠遠超出了我們實現它們的能力。當時還存在另一個問題,可能至今仍然存在,我不知道,那就是硬體非常昂貴。軟體方面,你無法真正估算成本,因此存在一種傾向,即硬體買得不足,並期望程式設計師來彌補差額。所以我們不僅有這些昂貴、非常小的機器,而且你試圖用它們做太多的事情,而我們根本不知道如何構建軟體。MITRE 是一家主要承接政府合約的公司,政府對這個軟體問題非常關注,因為在這種情況下,他們如何能夠構建他們感興趣的那種國防系統呢?於是我開始研究這個問題。程式方法學實際上感興趣的是,至少在當時,兩個不同且相當獨特的問題。其中一個是,程式應該如何設計?什麼是好的設計方法學?另一個是,程式應該如何建構?事實是當時沒有人有非常好的想法,或者至少當時開發軟體的方式,人們在這兩方面都做得不是很好。

影響深遠的論文

我現在要做的是告訴你們我讀過的一些論文,因為我為了這次演講回去重讀了它們。我發現這樣做真的很有趣。而且我認為,我相信你們中的許多人讀過這些論文,但我懷疑你們中的許多人沒有。我建議你們回去讀讀它們。我認為計算機科學作為一個學科在記憶我們的歷史方面做得不是很好,如果我們這樣做會是一件好事。好的,我不會告訴你們我讀過的每一篇論文,我也不會試圖說我漏掉的論文不是好論文。當時寫了很多非常有趣的論文。

這是我要談論的第一篇。而且,你知道,這就像重溫我們的過去,所以做起來很有趣,並且告訴你一些關於當時心態的事情。順帶一提,我應該說,當我向我的學生們講授這篇演講的初稿時,我發現他們對過去了解得很少,這很有趣。所以我要試著告訴你們一些關於過去的事情。

好的,Dijkstra 在 1968 年寫了這篇論文,叫做《The Go-To Statement Considered Harmful》。這篇論文是作為一封信寄給 Communications of the ACM 的。順帶一提,你們會在這些投影片中注意到的一件事是,有多少參考資料來自 CACM,因為當時 CACM 是首屈一指的期刊,最好的論文都發表在那裡。然後 ACM 做了一個不幸的決定,把它變成了一本公開的,你知道,小報。而現在,你們知道,大約三年前,他們扭轉了方向,現在他們正在努力把它變回一本嚴肅的期刊。

所以 Dijkstra 寫了這篇論文。它只有幾頁長,其中他論證了 go-to 語句不是個好主意。現在對你來說,這可能看起來根本不值得爭論。你知道,為什麼會這樣?事實上,這當時非常有爭議。我沒有費心回去閱讀許多針對 Dijkstra 說法而寫的論文。相反,我想為你們總結一下他到底說了什麼,並稍微談談為什麼人們反對。從今天的角度來看,Dijkstra 說的內容是顯而易見的。他說,你知道,我們必須推理程式的正確性。我們通過查看程式文本來做到這一點。但我們推理的是程式的執行,這是一個動態的事情。因此,為了試圖簡化推理過程,如果程式的執行方式能夠直接模擬文本所做的事情,那會是個好主意。如果你使用結構良好的控制結構,這將是真實的,你知道,如果你使用 if、while 和程序呼叫。但是如果你使用 go-to,這就不總是真實的。然後他繼續談論偵錯,他指出,你知道,假設你正在偵錯一個程式,並且你在程式碼執行的某個特定點,你正在試圖弄清楚這裡發生了什麼。好吧,為了回答這個問題,你必須要理解你是如何來到這裡的。如果你使用結構良好的控制結構,這不難做到,但是如果你使用 go-to,你可能來自任何地方。順帶一提,我想指出的是,在那些日子裡,人們甚至編寫不可重入(non-reentrant)的程式,我不得不維護這樣一個程式,它實際上修改了自己的程式碼,以便去掉一條指令,從而更快地進行分支。所以如果你認為 go-to 很糟糕,那種程式碼更糟糕。

我認為人們反對這個想法有兩個原因,也許是三個。好的,一個就是單純的老式保守主義。你知道,我們一直這樣做。第二個可能出於對效能的擔憂。但我認為第三個,也是可能最重要的原因,是當時的程式語言並沒有那麼好的控制結構。所以人們以各種方式使用標籤,在沒有合理的控制結構的情況下,這些用法實際上是相當合法的。例如,如果你將一個標籤作為參數傳遞給一個程序,你可以將其用作一種非常好的例外機制,如果發生例外,程序會跳到那個標籤。或者在沒有 case 語句的情況下,你可以使用標籤陣列來替代 case 語句。所以我認為當時的程式語言也缺乏表達能力,這是另一個讓大家擔憂的事情。

好的,我想談論的下一篇論文是 Nicholas Wirth 的這篇非常著名的論文《Program Development by Stepwise Refinement》。Wirth 對於如何設計程式這個問題很感興趣。這是一篇關於自上而下設計的論文。所以,你知道,W 方法學。我做了一個關於我對抽象資料型別的想法的演講。Steve Zillis 也在場,他寫了一篇類似想法的小論文。所以 Steve 和我開始一起工作。Steve Zillis 是 MIT 的一位研究生,但他是一位不尋常的研究生,因為他同時也是 IBM 的員工。他算是在半工半讀,他比普通的研究生更成熟,知道的更多。所以 Steve 和我開始一起工作。我非常喜歡那段時間。在我的記憶中從未下雨。Steve 和我每天一起吃午餐。當時我在 MIT 的 Tech Square 工作,那裡有一個很大的庭院,有樹和長椅。我們每天都會出去。我們會在外面吃午餐。我們會討論我們的想法。到那個夏天的結束,我想告訴你們我們當時在討論什麼。

所以 Steve 和我在研究這個。我想指出的第一件事是,我們兩個對程式語言了解很多。當然當時的程式語言不像現在這麼多。但你知道,有 Fortran 和 PL1。然後是整個 Algol 系列語言。然後是 Lisp。可能還有一些我現在想不起來的。我們兩個對這些語言都相當熟悉。例如 COBOL。

除此之外,當然我們也讀了很多文獻。這個時候我們讀的主要是程式語言文獻。所以你們可能不知道,過去有一種東西叫做可擴展語言。這有幾篇關於可擴展語言的早期論文。當時的情況是,程式語言界非常擔心,設計這些高階語言會從使用者那裡奪走太多的權力,所以必須提供一種方式將權力歸還給他們。可擴展語言就是朝這個方向的嘗試。Steve Schumann 和 Pierre Duran 的這篇第一篇論文是一篇調查論文,討論了人們正在研究的兩種不同方法。其中一種是他們稱為語法方法,他們會給程式設計師修改語言語法的權力。所以你知道,每個人都說「哦,這有道理」的那個小例子是,如果你一直想使用階乘,你可以使用一個小的階乘符號。

事實上,這是一個可怕的想法。實際上我想提到的是,另一個不同尋常的心態,也許不像我們希望的那樣不同,但不同尋常的是,人們當時非常專注於使程式易於編寫。而今天我認為我們已經轉變了,我們認為也許閱讀程式更重要。至少我肯定相信讓程式易於閱讀比讓它易於編寫重要得多,因為你編寫一個程式一次,你自己必須一遍又一遍地閱讀它。其他人必須閱讀它。最終會有人維護它。所以閱讀比編寫重要得多。如果使用者有太多機會搞亂語法,你可以想像這可能會讓閱讀程式變得非常困難。

Steve 和 Pierre 談論的另一種擴展是他們稱為語義擴展。這是一種小型的資料型別想法。所以把一個資料型別想像成只有一個定義,所以你可以說 stack 是 array with a... 所以它只是描述事物在儲存中如何佈局。但他們還有更多,例如在 Bob Balzer 1967 年的論文中,他提出每個資料型別都應該有四個操作。所以他正在考慮集合類型。這些操作是插入集合、從集合中移除、修改集合中的元素以及在集合中查詢。他進一步提出你可以使用語法來實現。所以如果你說 S := X,其中 S 是一個 stack,這將意味著 push。所以這部分非常糟糕。但有幾個操作的想法,好吧,這可能很有趣,只是沒有封裝(encapsulation)的概念。所以它只是一個方便的功能。而且,當然,這有點像緊身衣。資料有很多不同的類型,我們不可能只對四個操作感到滿意。

這是一篇關於 Simula 67 的論文。順帶一提,這本結構化程式設計的書,我推薦給你們。這就是我們所說的小黑書。它包含三篇非常有趣的論文。這是一篇。另一篇是 Dijkstra 關於自上而下設計的論文。第三篇是 Tony Hoare 關於資料結構的論文。所以這是一篇關於 Simula 67 的論文。Simula 當然有一種將資料物件的定義與對資料物件進行操作的操作相關聯的方式。但這並不是 Simula 開發的真正目的。它是為模擬而開發的。他們對層次化建構非常感興趣。而且沒有封裝。

好的,這是另一篇非常重要的論文。這是 Jim Morris 的論文,《Protection and Programming Languages》。他後來還有一篇論文進一步闡述了這些想法。Jim 對模組化程式建構非常感興趣。他關心如何推理程式的正確性。他希望能夠進行局部推理。所以他希望能夠拿起一個模組,並推理它的正確性,而無需擔心所有使用該模組的其他程式碼。這篇論文在某種意義上超前於時代,因為它真的隱藏著 rep invariants 和抽象函數的想法。他在這篇論文中指出,如果你的程式語言沒有正確的控制機制,你將無法進行這種局部推理。他擔心兩件事。其中一件是外部程式碼對物件的修改,畢竟,如果你可以修改物件,那你怎麼能對它們的外觀或意義有所說明呢?另一件事是偽裝(impersonation)。如果你可以創建一個聲稱是 stack 的東西,但它不是來自這段程式碼,那麼它可能不滿足約束。然後他繼續討論,好吧,我們如何強制執行呢?他提出了他所謂的 seal 和 unseal,這在某種程度上類似於加密操作。模組能夠將物件封裝起來。而沒有其他人知道如何封裝,所以存在一些密鑰。然後稍後,當一個物件進來時,它會解封。如果它不是它產生的東西,就會失敗。這就是他的想法。

好的,Steve 和我讀了所有這些論文。哎呀,還有一篇。好的,這是 Bill Wolf 和 Mary Shaw。這裡有趣的一點是,他們是與 CLU 同時開發出一種用於資料抽象的語言的另一個團隊。所以他們寫了這篇關於全域變數有害的論文。當然,標題與 Dijkstra 的標題相關。他們以 Dijkstra 的論文為類比進行推理。他們談論的是,在某種意義上,Algol 的程式設計風格,它有一個現在看起來非常幼稚的想法,就是你只寫一個龐大的程式。它將是一個巨大的外層區塊。然後會有內層區塊,內層區塊,再內層區塊。如果模組的部分需要通信,你只需要將它們通信的東西放在一個外層區塊中,它們就可以處理。Bill 和 Mary 指出,他們實際上在談論 Parnas 的想法。存在所有這些額外的連接。如果一個模組,除了它的形式參數外,還有其他它可以使用的東西,全域變數,那麼就很難對它進行推理。你無法真正理解它在做什麼。

好的,我們整個夏天都在工作。在夏天結束時,我們寫了這篇論文,並寄給了一個關於非常高階語言的 SIGPLAN 會議。所以現在是 1973 年秋天,我們成功地寫了這篇論文。我將稍微談談論文的內容。首先,它闡述了抽象資料型別的概念,指出封裝是這裡非常關鍵的一點。外部程式碼不能存取這些模組的內部,而且操作集是由型別的定義者認為應該是什麼樣子來決定的,而不是像可擴展語言那樣有一個預定義的操作集。而且操作是使用物件的唯一方式。它還提出了一個程式語言的草圖。所以它有一個實現抽象資料型別的機制。它想要多型(polymorphism)。所以它希望不僅能找到一個整數集合,還能找到一個 T 集合。它建議使用靜態型別檢查,雖然我們不太確定能否做到。原因是因為我們不確定這種多型是什麼,以及當你真正嘗試探索它時會如何表現。所以我們說,好吧,你總是可以使用 Jim Morris 的 seal 和 unseal 的想法。然後我們說,好吧,它應該有一個例外機制,我稍後會談到這個。

所以那是 1973 年秋天。在那時,我做了一個決定,也許我稍早一點就做出了,我實際上要繼續推進這個想法。所以我接下來幾年都在將論文中的那些想法轉變成一個真正的程式語言,這後來成為

CLU 語言的特性

將型別視為一組操作的想法在 cluster 機制中得到了非常直接的表達。在這張投影片的底部,我展示了如何使用抽象資料型別,而這張投影片有趣的地方在於這個美元符號($)的標記法。美元符號標記法本身有點粗糙,我現在大概不會像當時那樣使用完全相同的語法,但這裡實際上在表達一個重點,那就是 CLU 中的資料抽象觀點與物件導向語言中的觀點非常不同,因為我們認為操作屬於型別,而不是屬於物件。所以我在這裡非常直接地表達我正在呼叫 int set 型別的 create 操作。

現在,如果你考慮這兩種不同的方法,你可以看到它們各自的優點和缺點。如果你將操作放在型別中,這可以讓你在二元操作方面做得非常好,因為我有這種型別的實現,我可以接受兩個 int set 並輕鬆地檢查它們的各種比較。另一方面,它不支持層次結構或繼承,並且也不允許你在程式的某個部分內擁有同一型別的不同實現。

好的,現在我只給你們看一點點 cluster 的內部。這行 rep 在這裡,那是我們的實例變數。所以我們就是這樣定義實例變數的集合。我們通過型別定義來做到這一點。它通常是一個包含許多子組件的記錄(record)。我想給你們看的最後一件事只是其中一個操作的一小部分,這張投影片有趣的地方在於 create 標頭中使用了 CVT 這個詞。這有點像 Jim Morris 的 seal 和 unseal 的殘餘。所以這個想法是這些物件來自這個特定的 cluster,當它們出去時,我們通過 CVT 將它們封裝起來,所以我們將它們從 rep 型別轉換為抽象型別。然後我沒有向你們展示這個,但是一個接受物件作為參數的操作,我們也會在其中放入 CVT,這意味著從抽象型別到 rep 型別。然而,我們完全通過編譯時檢查來做到這一點,所以這沒有運行時開銷。

我們希望在 CLU 中實現多型。我們花了一點時間才弄清楚如何做到。所以這裡我有一個 set 而不是 inset,它有一個型別參數。在底部,我展示了 set 的使用。我們稱之為實例化,所以我說,好的,現在我想要一個整數的 set。

好的,問題是並非所有型別都適合成為 set 的元素。好的,現在你們可能被一些物件導向語言中發生的事情稍微誤導了,它們會說先驗(a priori)的物件擁有等於(equals),而且每個人都擁有等於,但 CLU 沒有這個概念,而等於只是一個例子。重點是,有時候參數化型別為了完成其工作,依賴於參數型別的某些屬性。在 set 的情況下,從根本上說,你必須有一種方式來比較元素是否相等。你需要一個相等關係,因為如果你將相同的元素放入 set 兩次然後再取出,你期望它會消失。

好的,我們沒有... 我們花了很長時間才找到解決方案,但我們想出的解決方案是這個 where 子句,我們只是列出了參數型別必須具備的操作,並給出了它們的簽名。然後最後,你可以使用這些操作。所以那個 where 子句就坐在使用者和實現者之間。無論何時你實例化該型別,我們會檢查確保型別參數滿足 where 子句,而在型別的實現內部,你能夠使用這些必需的操作。而實際的使用是在投影片底部的這一行,使用了等於符號。所以我們有一個適度的,我們稱為語法糖(syntactic sugar),你可以有一個簡單的語法形式,然後我們有一個直接的翻譯到美元符號標記法。

好的,這就是參數化型別。現在讓我稍微告訴你們例外處理。這是世界發生變化的另一個地方。好的,所以當時,例外處理是一個非常令人困惑的話題,John Goodenough 在 1975 年寫了這篇有趣的論文,其中他不僅闡述了當時的情況,還提出了一些解決方案。他指出,首先,人們使用兩種不同的模型進行例外處理,終止模型(termination model)和恢復模型(resumption model)。終止模型是我們都很熟悉的。恢復模型是一個程序拋出一個例外,它在其他地方被處理,處理完成後,控制權返回到該程序,然後程序繼續執行。實際上,在 PL1 中,它擁有恢復模型,你甚至可以讓它朝任何一個方向發展。所以你可以有一個恢復模型,但處理程序可以阻止程序繼續執行。當時沒有被很好地闡述的另一件事是關於如何指定處理程序的問題。所以今天我們很熟悉這在呼叫點發生,但在當時這只是幾種選擇之一。例如,你可以在程式碼區塊的頂部說,我希望這成為使用這個其他程序的所有情況的處理程序,諸如此類。

好的,在這樣的背景下,我們設計了 CLU 的機制,我應該說我們認為例外處理非常重要,一個原因是因為,首先,如果可能的話,你希望前提條件(preconditions)只是簡單的真,因為除此之外的任何情況都意味著模組和它的呼叫者之間有額外的連接。但是,當然,即使你不這樣做,通常情況下,參數領域的某些元素無法以處理其他元素的方式來處理。所以你需要一種好的方式來引起呼叫者的注意,而將其揉入某些回傳結果中並不是一個很好的處理方式。

所以,當然,choose 是一個非常好的例子,因為如果 set 是空的,你無法回傳 set 的元素。我這裡向你們展示的是它在 CLU 中是如何工作的。在程序的標頭中,我們會列出可能被引發的例外,然後在主體內部,我們可以發出信號(signal)它們。好的,然後在呼叫端,所以我們使用了呼叫點處理,所以我們談論的是終止呼叫點。好的,然後當我們呼叫 choose 時,我們可以處理那個例外。好的,這對你來說現在非常熟悉,因為這有點像我們現在處理事情的方式,但是在 CLU 中發生了一件事,我真的認為比現在發生的事情更好,這就是我將在這張投影片上談論的內容。這與在接收例外的程序中做什麼有關。所以實際上你可以做三件事。

首先,你可能會處理例外。所以我正在進行某種重複操作。我遇到了例外。這意味著我已經完成了程式的那一部分。我繼續進行下一部分。好的,另一件事你可能會做的是傳播(propagate),這意味著例外對你的呼叫者有不同的含義,所以你將它作為一個不同的例外拋出,這在你的規範層面上是有意義的。

但是還有第三件事,不幸的是,它總是存在,即使它很少發生,那就是例外本不應該發生。所以我剛才檢查了 set 不是空的。然後我呼叫了 choose,它拋出了 empty 例外。問題是,程式語言應該如何處理這個問題?程式語言對此能做的一件事是堅持你仍然編寫例外處理程式碼,但這並不是很好,因為這意味著你的整個程式會充滿這些無用的例外處理程式碼。程式語言可能做的另一件事是盲目地傳播它,這樣做的問題是,你永遠不知道一個程序的介面是什麼,因為它不是你列出的內容,還有所有這些其他東西。

所以 CLU 的解決方案是將這個例外,任何未處理的例外,轉換成一個特殊的例外,叫做 failure。對於你呼叫的任何程序來說,failure 總是另一種可能性,而且 CLU 的例外實際上帶有參數,failure 例外的參數是對導致 failure 發生的例外的描述。我認為這實際上是在程式語言中處理例外的一種非常好的方式。

好的,我想告訴你們的最後一件事是迭代器(iterators)。所以當我們設計 CLU 時,我們非常擔心如何處理這種類型的結構,這實際上是你真正想做的事情。擁有一個集合物件的意義是什麼?你將所有元素收集起來,然後你想用它們做點什麼。而這通常採取 for 迴圈的形式。

好的,在... 考慮到我到目前為止為你們描述的機制,我們只能看到兩種解決方案。其中一種是你可能會破壞集合。所以我可能有一個用於 sets 的 select 操作,它會移除元素,這樣我就可以遍歷 set 並迭代它,只是我可能不想破壞 set,因為我稍後可能還想用它做點什麼。另一件你可以做的事情是你可以使抽象變得複雜,所以我可以將它變成一個有序集合(ordered set)。這將給我一種遍歷它的方式,但是使抽象變得複雜不是一個好主意。你應該

類型層次結構與 Liskov 替換原則

對此感到非常困惑。我記得有一篇論文聲稱 stack 和 queue 是彼此的子類型,原因是他們發明了這樣一些中性的操作名稱,add 和 remove。所以它們都有 add 和 remove 操作,因此,你知道,看吧,你知道,好的。很顯然,思考也只進行到這一步,也許,你知道,我們需要討論操作,它們有名稱和簽名,故事就到此為止了。很顯然,這不是一個真實的陳述,因為如果我寫一個期望 stack 的程序,結果得到一個 queue,我會非常困惑,因為我期望的是 FIFO 與 LIFO。

好的。所以在這篇論文中,我提出了這個小小的陳述:子類型的物件如果通過超類型方法使用,應該像超類型的物件一樣行為。這有點常識。這是我在 1987 年 OOPSLA 上發表的演講。我將它寫成了一篇論文。後來發生的事情是它被傳開了。我發現五到十年後,網上有一個社群,他們都在討論他們稱之為 Liskov Substitution Principle,這至今仍在進行中。他們稱其為 LSP。這真是令人驚訝。但是,你知道,如果你看看它,它是直觀正確的。我後來與 Jeanette Wing 合作,我們試圖精確地形式化它的含義。

好的,我在那篇論文中還說了一件事。我說子類型對於多型是有用的。我在這張投影片的頂部顯示的是 CLU 的做法,使用了 where 子句。然後我這裡有一些假設性的記法,你用子類型替換操作列表。我實際上認為這是一個更好的機制。但有趣的是,在這兩種情況下,你都需要證明。在 CLU 中,你每次實例化時都需要證明,例如,equal 運算符確實是一個相等關係。而在子類型領域,你確實必須證明子類型符合 Liskov Substitution Principle。所以僅有語法是不夠的。你還必須具備語義。

反思與未來挑戰

好的,基本上就到這裡。所以當我做這種回顧時,我發現世界已經改變了。我主要是通過與我的學生交談來意識到這一點的,他們對過去的情況毫無概念,沒有雙關語。他們不知道區塊結構的程式。他們不知道類似 ALGOL 的語言。事實上,我記得在我獲得圖靈獎的時候,網上有人引用了一句話說,她憑什麼獲得那個獎?每個人都知道這個。所以世界已經改變了。

事實上,我的意思是,這太令人驚訝了,而且太美妙了。另一方面,生活也沒那麼美好。軟體危機仍然與我們同在。這個特定的真理更多是在違背中體現,而不是在... 你知道,那裡有很多編寫得非常好的程式,但也有巨大的爛攤子。最近我不得不了解一些關於網路服務程序的東西,我發現裡面充滿了全域變數。不僅如此,還有密碼放在那裡,我想他們只是希望沒有人意外地訪問它等等。所以是的,我們算是知道如何正確地做事情。我認為我們有真正的教育問題。

自從程式設計出現以來,這一直都是事實。任何人都可以寫程式,很多人認為他們不需要知道自己在做什麼。總之,事情並不是那麼好,軟體危機仍然與我們同在,即使我們是專業人士,並且知道我們在做什麼。所以在我看來,還有很多工作要做,而你們在程式語言界,我知道,正在為此努力。我已經等待了 30 年,期待新的抽象機制。你知道,我們有抽象資料型別,我們有迭代器,但是下一步是什麼?我一直在等待它的出現。如果我們能有更高層次的討論,我們就能以一種易於理解的方式表達更多內容,這將真正幫助我們編寫程式。

這兩個似乎是從我的系統角度來看很明顯迫在眉睫的問題。第一個是我們被告知大規模並行計算機即將到來。我並不完全相信這個說法,因為我不認為 Moore's Law 是上帝賦予的定律,如果它結束了,那麼,看吧,我們就必須在我們的機器中擁有數千個處理器。儘管如此,從研究角度來看,這是一個有趣的問題。毫無疑問,超級計算機是有用的,在你可以以資料並行方式解決的問題中,你確實可以利用它們。但是如果你把它們視為通用機器,我說的不是四個或八個處理器,我也不是在說你的圖形處理器等等,我是說,假設我有一個資料庫系統,我能在一千個處理器上有效地實現它嗎?如果我們確實要在這個領域取得任何進展,我們就需要新的範式來構建並行程式,因為這一直是一個漫長而悲傷的故事。也許 MapReduce 範式會有所幫助。也許 transactional memory,我的意思是,實際上,那又是 transaction 了。也許那會有所幫助,但我認為這裡有一個非常有趣的問題。我們如何編寫這些東西?

然後另一件我非常感興趣的事情是將 Internet 視為一台計算機。在這裡,你又看到它即將到來。他們提供雲端計算。儲存語義還沒有被很好地解決。實現,運行時語義還沒有被很好地解決。你如何為這些計算機編寫程式?事實上,我們在編寫分散式程式方面存在一個有趣的脫節。你編寫你自己的獨立模組,但當你想將它們連接在一起時,你就跳出了程式語言,進入了另一個世界。也許我們現在需要更完整的語言,以便我們能夠用語言編寫整個東西。總之,我就把這些留作許多開放的研究問題。非常感謝。

問答環節

Dave Unger: 您好,Barbara。我是來自 IBM Research 的 Dave Unger。關於 Liskov 替換原則,我有一個有趣的複雜性問題,我一直在努力解決。那就是如果一個類型提供了一組不變量(invariants),任何客戶端可能只需要其中的一個子集。所以有很多不變量,它們是很大的冪集(power sets)。因此,一個子類型對於某些客戶端來說可能是可替換的,但對於其他客戶端則不是,而我們目前的標準做法並不能真正捕捉到這個問題。您對此有什麼想法?

Liskov: 好吧,我完全沒有考慮過這個問題,所以我只能給您一個初步的想法,那就是我認為您這裡遇到的問題是關於規範(specification)的。所以您可以想像,如果您有一個... 所以我不確定這是否是一個... 如果我們把規範弄對了,程式語言就可以跟隨。所以如果您將一個類型視為擁有很多不變量,並且您將其視為寫在規範中,那麼您可以想像一個子類型,您可以擁有特定於不變量的子類型,然後您可以開始思考如何從那裡將您的程式組合起來。這就是我開始思考這個問題的方式。

Unger: 是的,這似乎挑戰了子類型這個概念的整體性,因為它們就變得是相對的了...

Liskov: 對的。所以它會是一種相對子類型,您不會將其視為一個完全的子類型,而是一個專門化的子類型。

Unger: 謝謝您。

Liskov: 不客氣。

Henry Berger: 您好。我是 Henry Berger,來自工業界。我對例外處理很感興趣。我的意思是,CLU 中的例外處理現在被廣泛使用並且似乎運作得很好,但正如您所說,隨著程式變得更加並行,而您正在並行和分散式系統領域工作,其中一個小部分的失敗不一定會導致整個程式崩潰,所以我想知道您對於在同時發生更多事情時處理例外有什麼看法?

Liskov: Argus 也有例外,它是一種並行語言,我記得的是關於 failure 例外的情況是您有點在回溯(backing up),其想法是您可能在更高層級處理它,可能是通過回溯到檢查點並拋棄一堆東西。我相信 Argus 中的想法是這將通過發生例外的進程(process)向上傳播,如果到最後仍未處理,那麼更大的單元,我們稱之為 guardian,從根本上說是一個物件,那麼您就必須使該 guardian 失敗。但是如果您可以在該進程的最高層級處理它,那麼它就不必使 guardian 失敗。您只需要擺脫該進程或修復該進程。不過,我不知道這是否是一個通用的答案,因為我們可能需要討論更多的例子。

Berger: 好的,但是您提到的 guardian,我猜它只是一個在一個處理器上運行的 guardian?

Liskov: 是的,一個 guardian 在一台機器上運行。它處理許多其他進程?

Liskov: 所以一個 guardian 就像,例如,您伺服器上的一個檔案系統介面。它將支持對檔案的所有訪問。如果發生了什麼問題,那沒關係,因為您可能有複製。所以您將它們組合在一起。所以是的,這是一種逐塊的方式,進程、guardian,而 guardian 是可重新啟動的,這通常是解決這些問題的方式。您使 guardian 失敗,然後重新啟動它。

... 然後重新啟動它。

Liskov: 我提出的處理方式是將其放入規範中。所以這就像一個額外的 requires 子句。有些迭代器要求迴圈體不能修改正在迭代的物件。其他的則無所謂。這就是我提出的處理方式。這絕對是一個問題。您必須捕捉到這種約束。

Berger: 謝謝您。

Liskov: 是的。

Peter Froelich: 您好,Peter Froelich,來自 Johns Hopkins。我本來想問您關於整個繼承部分的事情,因為您說 Smalltalk 當時還沒有出現,所以在 CLU 中沒有繼承。但您也引用了 Simula 67 的論文,其中有這種,在我看來,難以理解的區塊連接(block concatenation)的東西。您是故意決定不深入研究並將其排除在外嗎?還是像我一樣,在讀了六次之前無法理解?因為很多人根本無法理解連接的東西。所以這是故意的還是偶然的?

Liskov: 這是一個很難回答的問題。我對封裝(encapsulation)這個想法非常感興趣。所以我沒有太關注 Smalltalk,因為它們沒有封裝。而且我也可能受到了另一種限制,我真的很想專注於資料抽象的含義。所以就像我排除了並發(concurrency),我排除了繼承。但我沒有以像排除並發那樣明確的方式排除它。但我認為這不是因為缺乏理解,儘管也許是因為感覺 Smalltalk 沒有封裝,所以它們並不是我感興趣的東西。因此我沒有更多地關注它們。但現在可能不適合談,對吧?現在不適合。因為您可能從未聽說過它。其他任何想討論它的人,它是我的個人專案之一。非常感謝。

Froelich: 不客氣。

Carol Alphonse: 您好,Carol Alphonse,來自 University at Buffalo。我想知道您能否告訴我們您最初是如何對計算機產生興趣的,以及您對入學人數,特別是該領域的代表性不足有何看法。

Liskov: 是的,當然,當我還是學生的時候,女性的代表性幾乎為零。而今天,當然,我們在代表性不足的少數族裔方面代表性非常低。我偶然進入了程式設計。我大學畢業拿的是數學學位。我當時不想馬上讀研究所。我找不到一份有趣的數學家工作。於是我找到了一份程式設計師的工作。然後我發現我對此有天賦。幾年後,我決定去讀研究所。當時還很早,沒有那麼多提供程式設計學位的學校。我最終去了 Stanford。所以進入程式設計有點偶然。現在,我實際上是 MIT 的教務長助理,負責教職員工的公平問題,這意味著我特別關注女性和代表性不足的少數族裔教職員工。我認為,在計算機科學領域,雖然女性的代表性遠高於代表性不足的少數族裔,但我們真正的問題是人才管道(pipeline)問題。這個問題可以追溯到高中和國小。在大學層面,我們可以做的是注意我們的入門課程,確保它們不會被男性主導,這樣剛入學的年輕女性,那些可能過去十年沒有一直寫程式的學生,不會完全被教材壓倒。所以你可以解決這個問題。但除此之外,我們還有社會問題。而且我們在美國本來就有普遍的社會問題,因為很大一部分人對數學和科學抱持輕視態度。我認為我們甚至失去了男性學生,而且我們也失去了女性學生。所以大學層面能做的事情是有限的。我們在大學還能做的另一件事是努力培養有潛力的學生,關注年輕的女性和代表性不足的少數族裔學生,並試圖在研究中培養她們,這樣她們就會繼續讀研究所,並在計算機科學領域發展事業。但我認為,這大概就是我們能做的事情。

Alphonse: 非常感謝。

John Field: John Field,來自 IBM。我想知道您是否認為封裝可能會過多,特別是在擁有多種抽象機制(如繼承、物件、介面等)的語言中。

Liskov: 我不知道如何回答這個問題。正如我所說,我一直在系統領域。事實上,過去有很多關於領域特定語言(domain-specific languages)的工作。而且我認為,如果它是一個龐大的領域,並且您有一群訓練有素且都在這個領域工作的人,那麼為該領域提供一種專門的語言是可以的。我認為不好的情況是任何人和他兄弟都可以決定提供一些額外的語法,然後突然他們的程式就不再使用標準方言了。我認為這很糟糕,因為這樣您確實會失去可讀性。我也不是很確定人們是否足夠重視可讀性。即使沒有語法,閱讀程式也可能很困難,尤其是有很多繼承等等。

Field: 但如果物理學家需要一些專門的東西,因為他們是某個領域的專家,除非我們希望他們來設計語言,否則我們需要給他們一種可以擴展的語言。

Liskov: 是的,所以我認為,好吧,一種他們可以擴展的語言,或者我們可以為他們擴展。所以這就引出了一個問題,什麼是擴展?是語法的,還是只是更多的東西?

Field: 好的,謝謝。

Liskov: 是的。

William Cook: 您好,我是 William Cook。我想說我非常欣賞您的演講,我稍後也會發表一個演講,討論其中一些相同的問題。所以我覺得我們,我希望您能留下來,我們正在進行一場對話。而且我認為我們的觀點基本上是一致的,但我會對事情有不同的看法。我還想說我非常喜歡您的研究風格。我曾做過程式語言研究。我現在正在研究分散式系統,所以我覺得我在很多方面都在追隨您的腳步。所以我很欣賞您的領導力。最後我想說的是,我認為我必須稍微為繼承辯護一下。所以在某種程度上,我同意您的觀點,子類型和繼承是不同的,而且子類型可能更重要,但我認為繼承可能是物件導向程式設計中最容易被誤解的東西之一。而且我想我可以聲稱與此有點關聯,因為我有博士學位,我的論文題目就是繼承的語義學。所以我很想在某個時候告訴您那是什麼。但現在可能不適合談,對吧?現在不適合,因為您可能從未聽說過它。其他任何想討論它的人,它是我的個人專案之一。非常感謝。

Liskov: 不客氣。

Carlo Figaro: 您好,Carlo Figaro,來自 Universidad de Los Andes。抱歉再問一個程式語言問題。您對面向切面程式設計語言(aspect-oriented languages)和切面(aspects)作為模組化概念,作為新的模組化概念有什麼看法?

Liskov: 我對它們了解不多,這讓我有些擔憂。我知道 Gary 正在為此做很多工作,試圖弄清楚到底發生了什麼的語義學。擁有定義明確的語義以及能夠查看程式碼非常重要。所以我又回到了 Dijkstra 的論點。閱讀程式碼很重要。當您閱讀程式碼時,您必須有一個相當簡單的方式來理解您正在查看的內容,以及它與正在發生的事情之間的關係。我確實感覺到程式語言中有很多事情違反了這個原則,這讓我擔憂。但我無法告訴您關於特定機制的具體事情,因為正如我所說,我一直在系統領域,沒有投入太多注意力。

Figaro: 謝謝您。

Guy Steele: Guy Steele,Sun Labs。Barbara,我想感謝您強調歷史的價值。您列出的那些論文真是太棒了,我希望今天有更多人了解那些論文。所以我向房間裡的每個人推薦它們,如果您還沒有讀過的話。我的問題是關於您提到的 CLU 和 Alphard 之間的聯繫以及為 CLU 創建迭代器的問題。合併兩個抽象迭代器的問題是一個困難的問題。您選擇在 CLU 中限制這一點,而 Alphard 則面臨一個更困難的實現。這仍然是我今天在努力解決的問題。您有兩個抽象迭代器,正確的合併方式是什麼?在研究我目前的研究專案時,我回顧並研究了關於 CLU 和 Alphard 以及自那以後完成的工作的論文。我仍然不太確定正確答案是什麼。您能給我點提示嗎?您對這個問題還有進一步的想法嗎?

Liskov: 好吧,最終,我選擇了簡單直接、可以高效實現的機制。您總是可以使用其他方式。我的意思是,我們知道您可以使用物件來實現生成器(generators),所以我的看法是,在語言中提供能解決大部分問題的簡單直接的東西。這在程式語言設計中一直是一個問題,對吧?有 90% 的解決方案和 100% 的解決方案,而 90% 的解決方案可能從捕捉到大部分問題的角度來看要好得多,它更簡單,語義更直接,然後您將實現結構的責任留給使用者。所以您可以將其轉化為「這是一種實現它的方法」而不是「這是一個程式語言構造」。

Steele: 好的,謝謝您。也許我會選擇 90% 的解決方案。如果房間裡有人能為我找到 100% 的解決方案,我將不勝感激。

Liskov: 當然。

Steele: 謝謝您。

Bart Blum: Bart Blum,我開始從事計算機科學

如何推理程式的正確性。這應該,連同實際編寫一些程式,應該是我們大學部二年級或三年級課程的基礎部分。

Sri Ram: 您好,我是來自 Brown 的 Sri Ram。我想對歷史課表示感謝。我希望我在房間裡的學生像我一樣專心聽講,因為它真是太精彩了。您在演講中採取的一個堅定立場是明確表示您不喜歡語法擴展或語法抽象這個想法。所以我想稍微反駁一下。您給了一個答案,那就是不要做。另一個答案是,好吧,來找語言實現者讓他們為您做,這顯然不是一個可擴展的解決方案。但是還有第三個答案,那就是,聽到這個詞時可能會讓您震驚,模組化,對吧?為什麼您假設如果我進行語法擴展,它必須是全域範圍的?為什麼語法擴展不能像其他擴展一樣具有作用域?函式庫有作用域,API 有作用域。所以如果我們對語法抽象進行作用域限制,那麼我可以為自己構建一個,在我的程式中使用它,明確它的來源,它在哪裡被使用,就像我的語言中的任何其他抽象一樣。

Liskov: 好吧,我的意思是,我只能重複我說過的內容,那就是,你知道,如果你使用的符號非常明顯且直觀,那麼閱讀你程式的人可能不會有任何問題。但你真的需要考慮讀者以及明年的讀者。你真的希望程式的外觀看起來相當不言自明。這就是我不太熱衷於語法擴展的原因,無論是否有作用域。

Ram: 好的。謝謝。

Ralph Johnson: 我是來自 University of Illinois 的 Ralph Johnson。那是一個精彩的演講,我下學期將讓我的物件導向設計課的學生閱讀您的論文,因為我總會加入一些歷史背景部分,那真的太棒了。我想提出一個反對意見,針對您聲稱或您說 Smalltalk 沒有封裝。從類別的角度來看,這是對的。沒有類別定義的封裝,但物件的封裝非常強大。所以這有點取決於。我的意思是,這有點顯示您是個型別偏執者。從型別的角度來看,Smalltalk 沒有封裝的說法是對的。但如果您像我一樣是個物件偏執者,那麼這完全是錯誤的說法。所以我只是想澄清一下。

Liskov: 好吧,事實上,如果您還記得我說的話,我說的是就物件的使用者而言,沒有問題。我更擔心從子類別繼承的程式碼。

Johnson: 我只是想稍微修正一下那個說法。我想我實際上說了...

Liskov: 有一種解釋下它是真的,另一種解釋下它是假的。

Liskov: 我想我實際上說了 Smalltalk 從物件使用者的角度來看有封裝。無論如何,沒有封裝的是 Simula,至少在我做 CLU 工作的時候是這樣。不過,你知道,... 好吧...

Johnson: 總之,我要感謝您的精彩演講。

Liskov: 好的。