現在我們很難想象,曾經有段時間,在程式設計中使用遞迴的實用性甚至是可能性是受到懷疑的。然而,這種現象在1960年左右的程式設計社群中是真實的。創造了Algol 60的委員會也甚至在這個問題上存在分歧。遞迴如何進入程式語言是一個陰謀和誤解的故事。當我讀Gauthier van den Hove的優秀碩士論文[11]時,我第一次知道了這個故事。這也是[12]中第3章的主題。
在20世紀50年代末,成立了一個委員會,他們致力於設計一種通用的、與機器無關的程式語言。在當時,這樣一種語言並不是多餘的奢侈品:程式設計師們使用的程式設計系統由硬體製造商提供,這些程式設計系統甚至沒有在不同的模型間進行統一。Fortran語言是第一個例外,但它在當時仍然依賴於單一製造商。Lisp語言預示這某些事情的到來:與機器無關且不依賴於製造商。這就是Algol想要做的,但後來有更多的官方介入:在聯合國教科文組織的主持下成立的IFIP(國際資訊處理聯合會)資助了Algol。
McCarthy剛剛從他的Lisp專案中獲得成功,他熱衷於將遞迴作為一種優雅的方式讓計算機做它們最擅長的事:每次重複程式碼並做適當的修改。事實上,在最開始的Lisp語言中沒有迭代,因此新增線性表中所有元素的唯一方法是寫一個遞迴定義的函式。作為Algol 委員會的成員,McCarthy提出讓遞迴成為新語言的一個特徵的可能性。這個提案被更緊迫的問題所排擠。結果在1960年的前幾個月,當報告最終敲定時,大家在遞迴問題上並沒有達成共識。
這裡有充足的理由來反對。目前還不清楚它是否能實現:該委員會中的德國派認為,像Lisp直譯器這樣古怪的實驗研究對於可靠、高效的編譯器而言,並不是一個鼓舞人心的例子。但委員會成員Naur和van Wijngaarden贊同McCarthy的觀點,他們認為遞迴是一個太誘人的機會,不容錯過。Van Wijngaarden一直慫恿他的搭檔Edsger W. Dijkstra,誰贊成遞迴,誰就可能給他們提供新的、尚未發表的實現遞迴的想法。
Naur是編輯Algol報告最終版本的委員會成員。二十年後,Naur想起它來,如下[1]:
程式語言概念的最後一個重大的變化是允許遞迴程式啟用。這如下發生。 […] 大約在2月10日, […] 我接到了A. van Wijngaarden的電話,通話的人同時還有E.W. Dijkstra。他們指出在報告草案中缺少一個重要的定義,即程式識別符號在宣告中出現的意義不同於它出現在賦值語句的左邊部分。他們還明確表示,通過描述的規則防止遞迴啟用會使情況變得複雜,因為遞迴有可能通過程式及其引數間接啟用。他們建議在5.4.4節增加一個句子來澄清此事:“在程式中任何其他程式識別符號的出現都表示程式的啟用”。儘管考慮到這個問題隨後帶來麻煩的風險,我被這個建議的大膽和簡單所迷住,並決定這樣做。
結果發現在這個問題上確實存在麻煩。一些委員會成員已經被騙批准這個最終版本的報告,報告中包含一個後期加入的容易被忽略的內容,這在與委員們期望相反的方向上試圖解決爭論。委員會成員F.L. Bauer通過將語言中增加的遞迴視為“阿姆斯特丹陰謀”[1, 附錄5]來表明自己的抗議。
Dijkstra在2001年接受採訪時明確表明,這不是Bauer部分的偏執[2]:
遞迴是很重要的一步。它偷偷摸摸的被介紹。ALGOL-60報告草案在十二月最後一週被髮布。我們研究它,並意識到遞迴呼叫雖然沒有被說明,但已被允許。我打電話給Peter Naur—打到哥本哈根是我的第一個國際電話,我永遠不會忘記這種興奮!—並給他口頭提供了一個建議,而他在報告中包含了這個建議。這個建議是這樣的,“在程式中任何其他程式識別符號的出現都表示程式的啟用。”基本上就是這個情況。這個句子被暗中插入。並且所有人,包括沒有看到這句話的人,可以說都不得不同意這個報告。這就是遞迴是如何被明確列入的過程。
但是,是什麼原因Bauer沒有實現而Dijkstra實現了遞迴?事實上,“遞迴”意味著什麼呢?仔細看看Naur和Dijkstra對上述的說明,結果顯示Naur說“遞迴程式啟用”,這是一個執行時的概念;而Dijkstra說“遞迴”,這可以解釋為源程式的屬性。
這裡有一些澄清“遞迴”含義的嘗試。
- 它有可能在執行一個程式時被呼叫,這使得存在一個先前呼叫的啟用記錄。
- 可能通過呼叫被命名為形式引數的程式,直接或間接地呼叫自身。
- 間接通過呼叫未命名形式引數的程式,直接或間接地呼叫自身。
Naur想的是第一個含義,而Dijkstra想到的可能是第二個含義。
當Algol委員會中的Bauer派發現他們是阿姆斯特丹陰謀的受害者時,刪除有問題的句子,這似乎是定義他們的首選版Algol的一件簡單事情。他們發現這並不能消除遞迴。至少根據第一個含義不是遞迴。而這正是他們想要去掉的,因為他們希望靜態分配程式的啟用記錄。
公佈的Algol-60報告通過一些努力重新定義語言:SMALGOL [3], ECMA子集 [4], 和SUBSET Algol [5]。這些人基於他們的期望而聯合,共同禁止那些需要動態分配程式啟用記錄的方案。所有這三種語言都一致刪除了Algol-60報告5.4.4節中的最後一句。他們一致認為,對於消除遞迴這是不夠的,在這個方向上他們進行了不同的嘗試。例如,在Algol-60報告中最後新增了4.7.5.6節:
不呼叫程式本身,在執行任何程式中的語句和給實參賦值期間,可能發生通過名字呼叫相應形參的情況。在表示式賦值的期間,這也會發生在程式內部宣告中。
據我所知,Algol-60報告中沒有這句話。但定義子集的人像我一樣為他們提供了一個替代的表達:
不要寫遞迴程式。不要使用遞迴程式。
正如Dijkstra已經預見到的,他們應該加入:
不要嘗試對程式的引數做任何偷偷摸摸的事情。
這樣Algol 60失去了它的一大成就:對實施者和使用者而言的一個單一檔案。
對遞迴而言的一種有效治療是消除巢狀程式宣告並要求程式在第一次呼叫前宣告。我們看看這個,例如在C中,它已經被描述為“榮耀的組合語言”,更重要的是被Bauer派視為注重效率的典範成果。具有諷刺意味的是,這個效率典範的設計者認為在動態分配程式啟用記錄或允許程式呼叫自身兩個方面都沒有問題。無需修改,在呼叫前宣告的要求將消除相互遞迴的可能性。因為這不會帶來動態啟用記錄的問題,Ritchie放寬了之前使用定義的規則,允許程式頭部的冗餘宣告。以冗餘的預先宣告為代價,程式設計師能在C中定義相互遞迴的程式。
當然,我們不能責備Bauer派,因為後來的人沒有在這個領域的經驗。有趣的是,在1960年確實存在知識表明,當有人想要有吸引力的Algol程式機制時,避免動態分配的需要是多麼困難。這個程式機制與20世紀30年代以來眾所周知的lambda演算類似。根據當前的標準,Algol-60報告是一個及其緊湊的檔案。但與lambda演算的定義相比,它有些相形見拙。例如,在[6]中,lambda演算被定義86行,分佈在10個定義中。很明顯,在這個緊湊的定義中,lambda演算不允許遞迴函式的定義。然而在1935年,lambda演算的表達至少已經發現有兩個版本的Y組合子。而這個組合子使得遞迴函式可能在lambda 演算中定義。
一旦有人有一個簡單而通用的函式定義機制時,對於避免遞迴是多麼困難,lambda演算是另一個例子。同樣,我們不能責備Bauer派不知道這些事情。事實上,直到20世紀60年代,出版瞭如[7]這樣的書籍,使用遞迴的Y組合子才變得家喻戶曉。
這是一個安全的假設,阿姆斯特丹陰謀的肇事者並不知道。那為什麼他們如此肯定他們是在正確的道路上呢?我們知道,就其中的一個策劃者E.W. Dijkstra而言,他在1961年10月出版了數學中心的報告MR34“與機器無關的程式語言的設計”。在這篇報告中,Dijkstra當時考慮到相關性,提出了語言設計的一般原則,在我看來,今天仍然適用。一個安全的假設是,在一年之前,他已經確信這些原則。這些原則的普遍性使他有可能運用知識實現Y組合子,並隨後獲得有效實現動態分配程式啟用記錄的經驗。
在被引用最多的一篇論文中, F.P. Brooks [9]將有著狂熱追隨者的程式設計系統或語言與一系列無聊卻儘管可能有用,但沒有狂熱追隨者的專案進行對比。他指出,前者是由個人建立的,後者是由委員會建立的。他的言外之意是委員會設計的東西一定要歸到無聊的類別下。為了概念上的完整性,Brooks指明瞭區分的標準。因為委員會設計的東西一般被認為必定沒有概念上的完整性,Brooks將Algol 60放在後一類中。
通過Dijkstra,我從第二種觀點中走出來,並發現這一點[10, 第4頁]:
然後60年代與一個絕對的奇蹟,即ALGOL 60,一起開始。這是一個奇蹟,因為,一方面這種程式語言已經被一個委員會設計出來,而另一方面它的優點是那樣的突出,在回顧中,它已經被認為是“大多數繼承者中的一個重大的進步”(C. A. R. Hoare)。
奇蹟是怎樣發生的?對於Algol委員會這樣一個擁有各種成員的委員會而言,它引人注目地超越理想狀態,然後撰寫出簡明、完整的文件,並在概念的完整性上描述了一門語言,其優點得到Brooks的高度讚賞。這一切怎麼可能呢?
答案是這有一個醞釀期,我估計它開始於1955年10月在達姆施塔特舉辦的自動計算國際研討會。幾位發言者提出需要一種通用的、與機器無關的演算法語言。委員會的第一個繼承者開始做這項工作。他在美國接觸並尋找志同道合的人。這是一個歐洲/美國的聯合委員會,他們在蘇黎世會面,並商定這樣的語言標準。他們的文件製作得足夠詳細,以便該語言標準值得被稱為Algol 58。
這個報告的公佈引起了語言進一步發展的興趣。Peter Naur是加入Algol委員會的新成員之一。Algol 58是需要進一步發展的。許多新的想法被提出來。在一些方面,委員會超前的超越了現有最先進的理念和他們自己的理解力。
同時,委員會陷入困境。儘管Naur是新成員,他看到了結構化討論方式的需求,並建立了Algol會刊。他開始以語言定義的形式統一成果。他為了語法定義研究了Backus的形式主義,並將其應用在新語言不堪重負的語法清單上。為了趕上在1960年1月巴黎舉辦的最終設計會議上上交報告,他及時準備好了它。他沒有時間來準備政治。
Bauer1978年的回憶[1, 第41頁]:
… 解釋了巴黎ALGOL-60會議上一個奇怪的事件 … 在巴黎會議的開始,Peter Naur交給他們一份他的18頁的報告草案,這讓他們很驚訝。Peter Naur並沒有被委託做這件事,這是一個既成事實。因此,如果他寫的這個報告草案被“選”為討論的基礎,這聽起來充滿詩意;而在Peter Naur已經獲得這種優勢之後,該委員會只是被迫這麼做。同樣,他自動成為了編輯;這只是一個邀請他的禮貌問題。由於有一些擔心,他會利用這個位置在語言上發揮他自己的一些影響力(發生的事情的確如此,正如他表示的),這種發展被一些委員會成員認為是非常不健康的。
換句話說,一些委員會成員真的生氣了。在接下來的一頁:
但是,應當提到,不僅委員會成員之中存在懷疑態度,而且當編輯隨意修改會議的結果時,成員們無奈地表示沒有誰可以做些什麼。為了忠誠度,他被吞噬了。
然而,Bauer寫道:
在另一方面,儘管六天巴黎會議的時間進度非常緊迫,這樣的情況對獲得報告完成的草稿有一定的幫助。
這就解釋了奇蹟:在經過了Algol計劃的創造、探索和掙扎階段後,新人出現了並接管了它。前輩隱約意識到,那個人拯救了這個計劃,但他曾經受到傷害。Bauer可能永遠不會原諒Naur;儘管上述引用寫於1978年,在事情發生之後很久。對我而言,他們表明在某種程度上,Algol 60這個“奇蹟”是Naur創造的。
致謝
直到最近我才意識到在遞迴和同一程式的多條啟用記錄的可能性之間的區別。同樣,我認為Peter Naur只是Algol委員會中的一個成員,而他恰好正是個編輯。只是因為Gauthier van den Hove好心的給我看了一些他的研究成果,我才瞭解到這裡有引人入勝的故事。van den Hove先生最近發表了他的文章http://www.fibonacci.org/GHE7.3.pdf。非常感謝Paul McJones在一些方面的幫助。
參考文獻
[1] “The European Side of the Last Phase of the Development of Algol 60″ by P. Naur; page 3. ACM SIGPLAN Notices 13(8), pp. 13– 44 (1978)
[2] Oral History interview conducted by Philip L. Frana on August 2, 2001, Austin, Texas. OH 330, Charles Babbage Institute, University of Minneapolis.
[3] “Smalgol-61″, G.A. Bachelor, J.R.H. Dempster, D.E. Knuth, and J. Speroni, eds. Comm. ACM 4(11), pp. 499–502 (1961).
[4] “ECMA subset of Algol 60″ Comm. ACM 6(10), pp. 595–597 (1963).
[5] “Report on SUBSET Algol 60″ Comm. ACM 7(10), pp. 626–628 (1964).
[6] Introduction to Combinators and Lambda Calculus by G.R. Hindley and J.P. Seldin. Cambridge University Press, 1986.
[7] Denotational Semantics by Joseph Stoy.
[8] also Annual Review in Automatic Programming vol.3, Richard Goodman, ed., pp 27 — 42, Pergamon Press 1963.
[9] “No Silver Bullet: Essence and Accidents of Software Engineering” by F.P. Brooks. Computer, 20 (4), pp. 10–19 (1987).
[10] “Computing Science: Achievements and Challenges” by E.W. Dijkstra. ACM SIGAPP Applied Computing Review 7 (2), pp. 2–9, 1999.
[11] Edsger Wybe Dijkstra: First Years in Computing Science (1951–1968) by Gauthier van den Hove. MSc thesis, University of Namur, 2009.
[12] The Dawn of Software Engineering: from Turing to Dijkstra by Edgar Daylight. Lonely Scholar, 2012.