雜談現代高階程式語言

發表於2011-06-22

幾個月之前,Slashdot轉載了Robert Harper教授的一篇部落格,說卡內基梅隆大學計算機系把”物件導向程式設計“從大一新生的必修課中刪掉了,其原因是:

Object-oriented programming … is both anti-modular and anti-parallel by its very nature.(從其本質來講,物件導向程式設計既反模組化,又反並行。)

這兩個原因(anti-modular和anti-parallel)都是很重的指責了;尤其是anti-modular,因為OO的基本思想通常被理解成“封裝”,從而實現模組化。

我是在1995年第一次聽說“物件導向”(Object Oriented)這個說法。當時在學習正在成長過程中的C++,用的是Borland C++ 1.0。從那時開始的很多年裡,”類“(class)、“物件”(object)和“方法”(methods),以及在這些概念之上構建的”繼承 “(inheritance)和”多型“(polymorphism)都是我理解中OO最核心的思想。我猜大多數程式設計師在這方面的認識都和我差不多。

但是“封裝”真的是OO的本質嘛?直到最近為了給iPhone寫個玩具程式而學習Objective-C(一種非常古老和原始的物件導向程式語言)的時候,才注意到早在1998年,OO之父Alan Kay就曾經在一篇郵件中說,他很後悔發明了“object”這個詞,從而誤導大家,把注意力都集中到“封裝”,而忽視了OO的本質——messaging(訊息傳遞)。Alan Kay的原話是:

The big idea is “messaging”…. The key in making great and growable systems is much more to design how its modules communicate rather than what their internal properties and behaviors should be.

Objective-C的設計是非常強調“訊息傳遞”(messaging)的——對一個object的method的呼叫,被稱為“給這個 object發了一個訊息”。為了突出呼叫method時指定的引數(parameters)實際上是訊息中的一些內容,Objective-C不惜把 method的定義方式都做了相對於C的很大的修改,從而把引數嵌入在method的名字裡。比如在一個叫做myWebView物件中搜尋一段文字,要求 不區分大小寫,從前往後搜尋,用Objective-C來描述是:

[myWebView searchFor:myString direction:YES caseSensitive:NO wrap:YES]

而用C++或者Java來描述,則是

myWebView.searchFor(myString, YES, NO, YES)

乍看上去,C++或者Java的方式更簡短,但是Objective-C的方式更強調“發訊息”。實際上,上面Objective-C語句會被翻譯成如下C函式呼叫:

objc_msgSend(myWebView, searchFor:direction:caseSensitive:wrap:, myString, YES, NO, YES

從強調messaging的角度看,Objective-C確實比C++和Java更符合Alan Kay對OO思想的描述。

OO中的messaging思想不僅體現在Objective-C語言以及在其上構建的NextSTEP/Cocoa GUI程式設計套件上。在Cocoa因為Mac OS X和iPhone流行起來之前,很多人都接觸過Qt(一種基於C++語言的GUI開發套件)。Qt對messaing的支援比Objective- C/Cocoa更徹底——每個object可以發出若干signal,每個signal可以觸發這個object自己的或者其他objects的若干個 slot。

有意思的是,為了支援messaging,Qt對C++語言做了擴充套件,而Objective-C對C語言做了擴充套件。這兩套擴充套件都利用了起源於C 語言的“巨集”機制(macro)。類似的做法也可以用於Java,前提是我們在呼叫Java編譯器之前,先呼叫一下cpp巨集展開程式來預處理一下我們的 Java程式。這事兒可以留待Java愛好者們來搞?

不管是Objective-C還是Qt,都會“盡力”去檢查一個object是否支援一個method(或者叫message),但是並不禁止 程式設計師向一個object傳送一個它不認識的message(或者呼叫一個object沒有的method)。說“盡力”檢查,是因為兩者都不能保證檢查 的完備性。這是因為Objective-C和Qt都支援多型;具體的說,接受message的object可能是表示為一個指標(指向object),所 以直到執行時候,當一個message抵達某個object的時候,系統才能(通過查這個object對應的message list)知道這個object是否認識這個message。

這種靈活性在Google新推出的Go語言中也同樣實現了,而且做的很極致——Go語言中沒有class的概念;換句話說,不需要是class 型別的object才能有對應的方法(methods)——Go允許給幾乎任何型別附上methods。而且程式中可以很方便的檢測一個object是否 支援(一組)methods,比如:

說到這裡,我覺得差不多可以反過來理解Robert Harper教授對OO的評價了——其實Robert不是在藐視OO,而是在指責很多imperative OO languages(我理解包括Java和未經Qt擴充套件的C++;詳見後述),認為這些語言沒有完成實現OO中object messaging的核心思想,從而不算實現了“模組化“(modulization)的思想。

上述都是關於程式的模組化。實際上,模組化的另一個主要方面是對“資料”(data)的模組化。從圖靈機和lambda-calculus開 始,電腦科學家們就注意到程式和資料是統一的;比如在馮諾依曼的“二進位制儲存電子計算機”模型裡,程式和資料都是bit stream。即時我們在討論高階程式語言的時候,程式和資料也不應該被分開。因為現代資料操作和模組化的基礎是並行程式(parallelism),而 有效實現並行的基礎是程式的first-class表達,也就是把程式作為一種基本資料型別。

鑑於這篇帖子已經很長了,這段話就作為下一篇帖子的提綱吧。下一篇帖子裡,我們來說說XML、JSON、MessagePack、 Protocol Buffers這些persistent data structure,以及用源於古老的functional programming paradigm的Go語言和MapReduce實現的並行資料操作。

原文:Yi Wang

 

相關文章