新聞中心
想象一下,你坐在河邊,河岸上如茵綠草,不遠(yuǎn)處湍急河流;午后的陽光慵懶愜意,使人陷入冥想哲思,不覺開始思考眼前的河流是否真實(shí)存在。誠然,幾米外確實(shí)有河水奔流而下。不過,我們所稱為“河流”的存在究竟是什么呢?畢竟,河水奔流不息,一直處于變化之中。似乎,“河流”這個詞無法指代任何固定不變的事物。

專注于為中小企業(yè)提供成都網(wǎng)站建設(shè)、網(wǎng)站建設(shè)服務(wù),電腦端+手機(jī)端+微信端的三站合一,更高效的管理,為中小企業(yè)圍場免費(fèi)做網(wǎng)站提供優(yōu)質(zhì)的服務(wù)。我們立足成都,凝聚了一批互聯(lián)網(wǎng)行業(yè)人才,有力地推動了近1000家企業(yè)的穩(wěn)健成長,幫助中小企業(yè)通過網(wǎng)站建設(shè)實(shí)現(xiàn)規(guī)模擴(kuò)充和轉(zhuǎn)變。
2009 年,Clojure 的創(chuàng)始人 里奇·希基Rich Hickey 發(fā)表了 一場精彩的演講,探討了為什么上文那樣的哲學(xué)窘境會給面向?qū)ο蟪绦虻木幊谭妒綆黼y題。他認(rèn)為,人們看待計(jì)算機(jī)程序中的對象與看待河流的邏輯是一樣的:我們想象對象是固定不變的,即使對象的許多或者說全部的屬性都無時無刻不處于變化之中。所以,這種邏輯并不正確,我們無法區(qū)分在不同狀態(tài)下同一對象實(shí)例的不同之處。程序中沒有明確的時間的概念。人們只是單純地用著同一個名字,以期在引用對象時,對象能夠處于預(yù)期的狀態(tài)中。這樣,我們也就難免會遇到 故障bug。
希基總結(jié)道,這一難題的應(yīng)對辦法就是人們應(yīng)該將世界建模成作用于不可變數(shù)據(jù)的 進(jìn)程process 的集合,而不是可變的對象的集合。換句話說,我們應(yīng)把每個對象看作一條“河流”,因果相連??偨Y(jié)說來,你應(yīng)該使用 Clojure 等函數(shù)式語言。
作者在遠(yuǎn)足途中思考面向?qū)ο蟪绦蛟O(shè)計(jì)的本體論問題。
自從?;l(fā)表演講之后,人們對函數(shù)式編程語言的興趣不斷提升,主流的面向?qū)ο缶幊陶Z言也大多都采用了函數(shù)式編程語言。盡管如此,大多數(shù)程序員依舊沿用自己的老一套,繼續(xù)將對象實(shí)例化,不斷改變其狀態(tài)。這些人長此以往,很難做到用不同的視角看待編程。
我曾經(jīng)想寫一篇關(guān)于 Simula 的文章,大概會寫到我們今天所熟知的面向?qū)ο蟮睦砟钍呛螘r又是如何應(yīng)用到程序語言之中的。但是,我覺得寫當(dāng)初的 Simula 與如今的面向?qū)ο蟪绦蛟O(shè)計(jì)的 迥然不同之處,會更有趣一些,這我敢打包票。畢竟,我們現(xiàn)在熟知的面向?qū)ο蟪绦蛟O(shè)計(jì)還未完全成型。Simula 有兩個主要版本:Simula I 和 Simula 67。Simula 67 為世界帶來了 類class、 類的繼承class hierarchy 以及 虛擬方法virtual method;但 Simula I 是一個初稿,它實(shí)驗(yàn)了如何能夠?qū)?shù)據(jù)和進(jìn)程捆綁起來的其他設(shè)想。Simula I 的模型不是希基提出的函數(shù)式模型,不過這一模型關(guān)注的是隨時間展開的 進(jìn)程,而非有著隱藏狀態(tài)的對象之間的相互作用。如果 Simula 67 采用了 Simula I 的理念,那么我們?nèi)缃袼拿嫦驅(qū)ο蟪绦蛟O(shè)計(jì)可能會大有不同——這類偶然性啟示我們,不要想著現(xiàn)在的程序設(shè)計(jì)范式會一直占據(jù)主導(dǎo)地位。
從 Simula 0 到 Simula 67
Simula 是由兩位挪威人 克里斯汀·尼加德Kristen Nygaard 和 奧利-約翰·達(dá)爾Ole-Johan Dahl 創(chuàng)建的。
20 世紀(jì) 50 年代末,尼加德受雇于 挪威防務(wù)科學(xué)研究中心Norwegian Defense Research Establishment(NDRE),該研究中心隸屬于挪威軍方。在那里,他負(fù)責(zé)設(shè)計(jì) 蒙特卡洛模擬方法Monte Carlo simulations,用于核反應(yīng)堆設(shè)計(jì)與操作研究。最初,那些模擬實(shí)驗(yàn)是由人工完成的;后來,實(shí)驗(yàn)在 Ferranti Mercury 電腦 [1] 上編入程序運(yùn)行。尼加德隨后發(fā)現(xiàn),將這些模擬實(shí)驗(yàn)輸入電腦需要一種更有效的方式。
尼加德設(shè)計(jì)的這種模擬實(shí)驗(yàn)就是人們所知的“離散事件模型discrete event model”,這種模擬記錄了一系列事件隨著時間改變系統(tǒng)狀態(tài)的進(jìn)程。但是問題的關(guān)鍵在于模擬可以從一個事件跳躍到另一個事件中,因?yàn)槭录请x散的,事件之間的系統(tǒng)不存在任何變化。根據(jù)尼加德和達(dá)爾在 1966 年發(fā)表的一篇關(guān)于 Simula 的論文,這種模型被迅速應(yīng)用于“神經(jīng)網(wǎng)絡(luò)、通信系統(tǒng)、交通流量、生產(chǎn)系統(tǒng)、管理系統(tǒng)、社會系統(tǒng)等” ?[2]? 領(lǐng)域的分析。因此,尼加德認(rèn)為,其他人描述模擬實(shí)驗(yàn)時,可能也需要更高層級的模型。于是他開始物色人才,幫助他完成他稱之為“模擬語言Simulation Language”或者“蒙特卡洛編譯器Monte Carlo Compiler”的項(xiàng)目 [3]。
達(dá)爾當(dāng)時也受雇于挪威防務(wù)科學(xué)研究中心,專攻語言設(shè)計(jì),此時也加入了尼加德的項(xiàng)目,扮演“沃茲尼亞克”的角色(LCTT 譯注:指蘋果公司聯(lián)合創(chuàng)始人斯蒂夫·蓋瑞·沃茲尼亞克)。在接下來一年左右的時間,尼加德和達(dá)爾攜手開發(fā)了 Simula 0 語言。[4]? 這一語言的早期版本僅僅是在 ALGOL 60 基礎(chǔ)上進(jìn)行的較小拓展,當(dāng)時也只是打算將其用作預(yù)處理程序而已。當(dāng)時的語言要比后來的編程語言抽象得多,其基本語言結(jié)構(gòu)是“車站stations”與“乘客customers”,這些結(jié)構(gòu)可以用于針對具體某些離散事件網(wǎng)絡(luò)建立模型。尼加德和達(dá)爾給出了一個模擬飛機(jī)離港的例子。[5] 但是尼加德和達(dá)爾最后想出了一個更加通用的語言結(jié)構(gòu),可以同時表示“車站”和“乘客”,也可以為更廣泛的模擬建立模型。這是兩個主要的概括,它改變了 Simula 作為 ALGOL 專屬包的定位,使其轉(zhuǎn)變?yōu)橥ㄓ镁幊陶Z言。
Simula I 沒有“車站stations”和“乘客customers”的語言結(jié)構(gòu),但它可以通過使用“進(jìn)程process”再現(xiàn)這些結(jié)構(gòu)。(LCTT 譯注:此處使用的“進(jìn)程”,與當(dāng)前計(jì)算機(jī)中用來指代一個已執(zhí)行程序的實(shí)體的概念不同,大致上,你可以將本文中所說的“進(jìn)程”理解為一種“對象”。)一個進(jìn)程包含大量數(shù)據(jù)屬性,這些屬性與作為進(jìn)程的 操作規(guī)程 的單個行為相聯(lián)系。你可能會把進(jìn)程當(dāng)作是只有單個方法的對象,比如 run() 之類的。不過,這種類比并不全面,因?yàn)槊總€進(jìn)程的操作規(guī)程都可以隨時暫停、隨時恢復(fù),因?yàn)檫@種操作規(guī)程屬于 協(xié)程coroutine 的一種。Simula I 程序會將系統(tǒng)建立為一套進(jìn)程的模型,在概念上這些進(jìn)程并行運(yùn)行。實(shí)際上,一個時間點(diǎn)上能稱為“當(dāng)前進(jìn)程”的只有一個進(jìn)程。但是,一旦某個進(jìn)程暫停運(yùn)行,那么下一個進(jìn)程就會自動接替它的位置。隨著模擬的運(yùn)行,Simula 會保持一個 “事件通知event notices” 的時間線,跟蹤記錄每個進(jìn)程恢復(fù)的時間。為了恢復(fù)暫停運(yùn)行的進(jìn)程,Simula 需要記錄多個 調(diào)用棧call stacks 的情況。這就意味著 Simula 無法再作為 ALGOL 的預(yù)處理程序了,因?yàn)?ALGOL 只有一個 調(diào)用棧call stacks。于是,尼加德和達(dá)爾下定決心,開始編寫自己的編譯器。
尼加德和達(dá)爾在介紹該系統(tǒng)的論文中,借助圖示,通過模擬一個可用機(jī)器數(shù)量有限的工廠,闡明了其用法。[6] 在該案例中,進(jìn)程就好比訂單:通過尋找可用的機(jī)器,訂單得以發(fā)出;如果沒有可用的機(jī)器,訂單就會擱置;而一旦有機(jī)器空出來,訂單就會執(zhí)行下去。有一個訂單進(jìn)程的定義,用來實(shí)例化若干種不同的訂單實(shí)例,不過這些實(shí)例并未調(diào)用任何方法。該程序的主體僅僅是創(chuàng)建進(jìn)程,并使其運(yùn)行。
歷史上第一個 Simula I 編譯器發(fā)布于 1965 年。尼加德和達(dá)爾在離開挪威防務(wù)科學(xué)研究中心之后,就進(jìn)入了 挪威計(jì)算機(jī)中心Norwegian Computer Center 工作,Simula I 也是在這里日漸流行起來的。當(dāng)時,Simula I 在 UNIVAC 公司的計(jì)算機(jī)和 Burroughs 公司的 B5500 計(jì)算機(jī)上均可執(zhí)行。[7] 尼加德和達(dá)爾兩人與一家名為 ASEA 的瑞典公司達(dá)成了咨詢協(xié)議,運(yùn)用 Simula 模擬加工車間。但是,尼加德和達(dá)爾隨后就意識到 Simula 也可以寫一些和模擬完全不搭邊的程序。
奧斯陸大學(xué)University of Oslo教授 斯坦因·克羅達(dá)爾Stein Krogdahl 曾寫過關(guān)于 Simula 的發(fā)展史,稱“真正能夠促使新開發(fā)的通用語言快速發(fā)展的催化劑”就是 一篇題為《記錄處理》Record Handling的論文?,作者是英國計(jì)算機(jī)科學(xué)家 查爾斯·安東尼·理查德·霍爾C.A.R. Hoare。[8] 假如你現(xiàn)在讀霍爾的這篇論文,你就不會懷疑這句話。當(dāng)人們談及面向?qū)ο笳Z言的發(fā)展史時,一定會經(jīng)常提起霍爾的大名。以下內(nèi)容摘自霍爾的《記錄處理》一文:
該方案設(shè)想,在程序執(zhí)行期間,計(jì)算機(jī)內(nèi)部存在任意數(shù)量的記錄,每條記錄都代表著程序員在過去、現(xiàn)在或未來所需的某個對象。程序?qū)ΜF(xiàn)有記錄的數(shù)量保持動態(tài)控制,并可以根據(jù)當(dāng)前任務(wù)的要求創(chuàng)建新的記錄或刪除現(xiàn)有記錄。
計(jì)算機(jī)中的每條記錄都必須屬于數(shù)量有限但互不重合的記錄類型中的一類;程序員可以根據(jù)需要聲明盡可能多的記錄類型,并借助標(biāo)識符為各個類型命名。記錄類型的命名可能是普通詞匯,比如“?!薄ⅰ白雷印币约啊胺孔印?,同時,歸屬于這些類型的記錄分別代表一頭“?!?、一張“桌子”以及一座“房子”。
霍爾在這片論文中并未提到子類的概念,但是達(dá)爾由衷地感謝霍爾,是他引導(dǎo)了兩人發(fā)現(xiàn)了這一概念。[9]? 尼加德和達(dá)爾注意到 Simula I 的進(jìn)程通常具有相同的元素,所以引入父類來執(zhí)行共同元素就會非常方便。這也強(qiáng)化了“進(jìn)程”這一概念本身可以用作父類的可能性,也就是說,并非每種類型都必須用作只有單個操作規(guī)程的進(jìn)程。這就是 Simula 語言邁向通用化的第二次飛躍,此時,Simula 67 真正成為了通用編程語言。正是如此變化讓尼加德和達(dá)爾短暫地萌生了給 Simula 改名的想法,想讓人們意識到 Simula 不僅僅可以用作模擬。?[10] 不過,考慮到 “Simula”這個名字的老牌度已經(jīng)很高了,另取名字恐怕會帶來不小的麻煩。
1967 年,尼加德和達(dá)爾與 控制數(shù)據(jù)公司Control Data 簽署協(xié)議,著手開發(fā)Simula 的新版本:Simula 67。同年六月份的一場會議中,來自控制數(shù)據(jù)公司、奧斯陸大學(xué)以及挪威計(jì)算機(jī)中心的代表與尼加德和達(dá)爾兩人會面,意在為這門新語言制定標(biāo)準(zhǔn)與規(guī)范。最終,會議發(fā)布了 《Simula 67 通用基礎(chǔ)語言》,確定了該語言的發(fā)展方向。
Simula 67 編譯器的開發(fā)由若干家供應(yīng)商負(fù)責(zé)。Simula 用戶協(xié)會The Association of Simula Users(ASU)也隨后成立,并于每年舉辦年會。不久,Simula 67 的用戶就遍及了 23 個國家。[11]
21 世紀(jì)的 Simula 語言
人們至今還記得 Simula,是因?yàn)楹髞砟切┤〈木幊陶Z言都受到了它的巨大影響。到了今天,你很難找到還在使用 Simula 寫程序的人,但是這并不意味著 Simula 已經(jīng)從這個世界上消失了。得益于 GNU cim,人們在今天依然能夠編寫和運(yùn)行 Simula 程序。
cim 編譯器遵循 1986 年修訂后的 Simula 標(biāo)準(zhǔn),基本上也就是 Simula 67 版本。你可以用它編寫類、子類以及虛擬方法,就像是在使用 Simula 67 一樣。所以,用 Python 或 Ruby 輕松寫出短短幾行面向?qū)ο蟮某绦?,你照樣也可以?cim 寫出來:
! dogs.sim ;
Begin
Class Dog;
! The cim compiler requires virtual procedures to be fully specified ;
Virtual: Procedure bark Is Procedure bark;;
Begin
Procedure bark;
Begin
OutText("Woof!");
OutImage; ! Outputs a newline ;
End;
End;
Dog Class Chihuahua; ! Chihuahua is "prefixed" by Dog ;
Begin
Procedure bark;
Begin
OutText("Yap yap yap yap yap yap");
OutImage;
End;
End;
Ref (Dog) d;
d :- new Chihuahua; ! :- is the reference assignment operator ;
d.bark;
End;
你可以按照下面代碼執(zhí)行程序的編譯與運(yùn)行:
$ cim dogs.sim
Compiling dogs.sim:
gcc -g -O2 -c dogs.c
gcc -g -O2 -o dogs dogs.o -L/usr/local/lib -lcim
$ ./dogs
Yap yap yap yap yap yap
(你可能會注意到,cim 先將 Simula 語言編譯為 C 語言,然后傳遞給 C 語言編譯器。)
這就是 1967 年的面向?qū)ο蟪绦蛟O(shè)計(jì),除了語法方面的不同,和 2019 年的面向?qū)ο蟪绦蛟O(shè)計(jì)并無本質(zhì)區(qū)別。如果你同意我的這一觀點(diǎn),你也就懂得了為什么人們會認(rèn)為 Simula 在歷史上是那么的重要。
不過,我更想介紹一下 Simula I 的核心概念——進(jìn)程模型。Simula 67 保留了進(jìn)程模型,不過只有在使用 Process? 類 和 Simulation 塊的時候才能調(diào)用。
為了表現(xiàn)出進(jìn)程是如何運(yùn)行的,我決定模擬下述場景。想象一下,有這么一座住滿了村民的村莊,村莊的旁邊有條小河邊,小河里有很多的魚。但是,村里的村民卻只有一條魚竿。村民們胃口很大,每隔一個小時就餓了。他們一餓,就會拿著魚竿去釣魚。如果一位村民正在等魚竿,另一位村民自然也用不了。這樣一來,村民們就會為了釣魚排起長長的隊(duì)伍。假如村民要等五、六分鐘才能釣到一條魚,那么這樣等下去,村民們的身體狀況就會變得越來越差。再假如,一位村民已經(jīng)到了骨瘦如柴的地步,最后他可能就會餓死。
這個例子多少有些奇怪,雖然我也不說不出來為什么我腦袋里最先想到的是這樣的故事,但是就這樣吧。我們把村民們當(dāng)作 Simula 的各個進(jìn)程,觀察在有著四個村民的村莊里,一天的模擬時間內(nèi)會發(fā)生什么。
完整程序可以通過此處 ??GitHub Gist?? 的鏈接獲取。
我把輸出結(jié)果的最后幾行放在了下面。我們來看看一天里最后幾個小時發(fā)生了什么:
1299.45: 王五餓了,要了魚竿。
1299.45: 王五正在釣魚。
1311.39: 王五釣到了一條魚。
1328.96: 趙六餓了,要了魚竿。
1328.96: 趙六正在釣魚。
1331.25: 李四餓了,要了魚竿。
1340.44: 趙六釣到了一條魚。
1340.44: 李四餓著肚子等著魚竿。
1340.44: 李四在等魚竿的時候餓死了。
1369.21: 王五餓了,要了魚竿。
1369.21: 王五正在釣魚。
1379.33: 王五釣到了一條魚。
1409.59: 趙六餓了,要了魚竿。
1409.59: 趙六正在釣魚。
1419.98: 趙六釣到了一條魚。
1427.53: 王五餓了,要了魚竿。
1427.53: 王五正在釣魚。
1437.52: 王五釣到了一條魚。
可憐的李四最后餓死了,但是他比張三要長壽,因?yàn)閺埲€沒到上午 7 點(diǎn)就餓死了。趙六和王五現(xiàn)在一定過得很好,因?yàn)樾枰~竿的就只剩下他們兩個了。
這里,我要說明,這個程序最重要的部分只是創(chuàng)建了進(jìn)程(四個村民),并讓它們運(yùn)行下去。各個進(jìn)程操作對象(魚竿)的方式與我們今天對對象的操作方式相同。但是程序的主體部分并沒有調(diào)用任何方法,也沒有修改進(jìn)程的任何屬性。進(jìn)程本身具有內(nèi)部狀態(tài),但是這種內(nèi)部狀態(tài)的改變只有進(jìn)程自身才能做到。
在這個程序中,仍然有一些字段發(fā)生了變化,這類程序設(shè)計(jì)無法直接解決純函數(shù)式編程所能解決的問題。但是正如克羅達(dá)爾所注意到的那樣,“這一機(jī)制引導(dǎo)進(jìn)行模擬的程序員為底層系統(tǒng)建立模型,生成一系列進(jìn)程,每個進(jìn)程表示了系統(tǒng)內(nèi)的自然事件順序?!盵12] 我們不是主要從名詞或行動者(對其他對象做事的對象)的角度來思考正在進(jìn)行的進(jìn)程。我們可以將程序的總控制權(quán)交予 Simula 的事件通知系統(tǒng),克羅達(dá)爾稱其為 “時間管理器time manager”。因此,盡管我們?nèi)匀辉谶m當(dāng)?shù)馗淖冞M(jìn)程,但是沒有任何進(jìn)程可以假設(shè)其他進(jìn)程的狀態(tài)。每個進(jìn)程只能間接地與其他進(jìn)程進(jìn)行交互。
這種模式如何用以編寫編譯器、HTTP 服務(wù)器以及其他內(nèi)容,尚且無法確定。(另外,如果你在 Unity 游戲引擎上編寫過游戲,就會發(fā)現(xiàn)兩者十分相似。)我也承認(rèn),盡管我們有了“時間管理器”,但這可能并不完全是?;囊馑?,他說我們在程序中需要一個明確的時間概念。(我認(rèn)為,?;胍念愃朴?nbsp;?阿達(dá)·洛芙萊斯Ada Lovelace 用于區(qū)分一個變量隨時間變化產(chǎn)生的不同數(shù)值的上標(biāo)符號?。)盡管如此,我們可以發(fā)現(xiàn),面向?qū)ο蟪绦蛟O(shè)計(jì)前期的設(shè)計(jì)方式與我們今天所習(xí)慣的面向?qū)ο蟪绦蛟O(shè)計(jì)并非完全一致,我覺得這一點(diǎn)很有意思。我們可能會理所當(dāng)然地認(rèn)為,面向?qū)ο蟪绦蛟O(shè)計(jì)的方式千篇一律,即程序就是對事件的一長串記錄:某個對象以特定順序?qū)ζ渌麑ο螽a(chǎn)生作用。Simula I 的進(jìn)程系統(tǒng)表明,面向?qū)ο蟪绦蛟O(shè)計(jì)的方式不止一種。仔細(xì)想一下,函數(shù)式語言或許是更好的設(shè)計(jì)方式,但是 Simula I 的發(fā)展告訴我們,現(xiàn)代面向?qū)ο蟪绦蛟O(shè)計(jì)被取代也很正常。
分享標(biāo)題:Simula誕生之前的面向?qū)ο蟪绦蛟O(shè)計(jì)
網(wǎng)站路徑:http://m.fisionsoft.com.cn/article/dpepphs.html


咨詢
建站咨詢
