小tao給我找了一個非常好的題目:

so, you think the consistent thing means a lot? But how do you think the gracious or elegant feeling mean to the real expressiveness of business requirements? We can see lisp is very successful in researching area because of the simplicity nature inside which is also true to Smalltalk. But why they aren't successful in biz world? I think that is the problem you should further study, "how can the consistent feeling mean something to our everyday programming life?".


我承認以前沒有考慮到這個問題,因為我以為追求概念一致性是一種不言自明的天性。那么為什么要一致性很重要呢?我有這樣一些想法。

1. 復雜性

概念不一致產生首先產生的一個惡果就是復雜性,這里我將舉兩個例子。

第一個關于Lisp的。Lisp本身具有一個很一致很簡單的計算模型——λ 演算。在這個計算模型中,基本元素是函數,λ運算符,·運算符。語義也是簡單明確的,lambda運算符提取出函數中的自由變量,然后再由apply對自由變量賦值。整個計算過程就是函數(符號)求值的過程。例如對于函數x + y,我們可以這樣來求值。

((λx (λy . x + y)·34)
=((λx. x+34)
=4 + 3
=7

由于Lisp多少可以認為是λ 演算的一種實現,我們可以類似寫出Lisp代碼

(define f (lambda x (lambda y (+ x y))))
(apply (apply f 
34)

或者更加簡單的寫為

(define (f x y) (+ x y))
(f 
3 4)

所有在Lisp中的程序,我們都可以用一種一致的概念來表達,就是符號求值,我們對一個符號應用一些值,然后這個符號完成計算并把結構返回給我們,在計算的過程中,符號對外部環境沒有任何破壞和依賴。但是Lisp中引入了賦值,于是存在一些符號,不僅僅是來計算,同時他們還能改變環境。

(define (bad-f x y) 
  (begin 
     (set
! z 5)
     (
+ x y)))

這個函數不僅僅求x + y的值,同時修改了符號Z的值。賦值的引入破壞了函數只求值而不影響環境這個概念的一致性。于是我們不得不尋找一個更加復雜的也更加混亂的計算模型,來代替簡單優雅的λ 演算。于是不一致的概念帶來了思維上的復雜性(不過Lisp中的概念不一致除了產生了復雜之外,還激發了人們對于簡單計算模型的向往從而產生了函數式編程風格,這也是我最愛的編程風格之一)。

概念不一致帶來的復雜性距離我們最近的應該是Java中的簡單類型,Java本身是一個單根的面向對象語言,從概念上講一切都應該是對象,而且由于性能考慮而出現的簡單類型,引入了代數性的世界觀的同時,破壞了面向對象的一致概念。Java中雖然有包裝類來做補償,但是仍然不能彌補概念上的斷裂。我們可以用

1new Integer(3)

來代表對象概念中的3,但是

3 + 4 * 5

不能對等的翻譯為:

1new Integer(3+ new Integer(4* new Integer(5)

簡單類型具有的運算符和對象類型的消息發送概念上是不一致的。雖然我們可以在Java 5中使用上述的代碼,但是Java 5的Auto-Boxing更多的把對象類型當作簡單類型進行數據運算,而不是像C++中的運算符重載一樣把數據運算當作一種特殊消息傳遞,雖然語法上有相似的功能,但是概念上大異其趣,對于我這個OO分子而言,Java 5的Auto-Boxing實在是惡心到不行。同時簡單類型和對象類型在賦值語義和存儲模型上也存在很大的不一致,這種不一致性在使用Collection API的時候帶來了一些復雜度。(還記得Commons Collection里那些Primitive Collection吧?)

2.副作用

概念不一致產生的第二個惡果就是副作用,我大概想了一下都有哪些可能的副作用,結果讓我很寒,我能想到的副作用,大多數是由于在另一種模型中引入了馮語言的因素從而造成了概念上的不一致而引起的。其中最為人知的....還是賦值在函數式編程中的副作用...這個在函數式編程社區有很廣泛的討論,我只說一點,為什么命令語言是有害的。

馮語言或者說命令式語言主要用于刻畫對計算機操作的序列,并不關心序列的內在關系。也就是說,我們可以把一些完全沒有聯系的指令寫在一起。因此馮語言在語義上毫無建樹,同時在抽象上也止步于子程序(一砣指令和另一砣指令)。馮語言具有很強的時間耦合性在存儲上也強烈的傾向于線性存儲。目前大多數難處理的問題,可以歸結為計算模型和操作語義的不一致,甚至有些學者認為是馮結構嚴重制約了計算機的發展。因此馮語言被函數社區看作一個很危險的副作用,極力避免之。

概念的不一致產生的副作用在混血的OO語言中也很明顯,尤其是在命令式語言中加入面向對象機制,而且使用面向對象作為類型系統的擴展,使用過程式的方法來控制流程。過程和對象的不一致本身就給了程序員一種暗示,你可以使用對象技術來構造類型系統,然后使用過程的方法來操作他。明顯的副作用們很容易使用過程化程序,從而使得對象帶來的好處被很大的削弱了(當然我們有重構來彌補這個錯誤),比如下面一個例子,我們很容易從寫出下面的代碼:

1if (order.getState() == Order.CANCEL)
2   do something for cancel;
3else if(order.getState() == Order.PAID)
4   do something for paid;
5else if(order.getState() == Order.DELIVERY)
6   do something for delivery

而不是

 1public abstract OrderState {
 2
 3   protected Order _order;
 4   
 5   protected OrderState(Order order){
 6      _order = order;
 7   }

 8    
 9   public abstract void handler();
10}

11
12class CancelState extends OrderState {
13
14   public void handler() {
15      do something for cancel;
16   }

17}

18
19order.getState().handle();

因為存在過程性的關鍵字if, while,處于方便的考慮,我們會寫出第一種風格的代碼,而考慮到易于維護,我們應該寫第二種風格的代碼。存在兩種不一致的書寫代碼的風格,這個時候,副作用取決于使用的語言OO的程度,比如在C++里更容易寫出第一種風格的代碼,而在Ruby和Smalltalk中,更容易寫出第二種風格的代碼。

3.遞歸的構造軟件

這個雖然跟概念一致有關,但是一個更大的主題,待續吧。

4.優雅,美感和無名品質

概念的一致性會產生一種優雅的美感,我覺得我們都有這種感覺,如果我們能用一種一致的容易理解且統一的方式來處理多種不同的復雜的問題,那么我們就會覺得這種方法很優雅,也很具有美感。類比物理中的牛頓定律,開普勒定律,麥克斯韋方程,都是這種簡單一致而解決復雜問題的完美示例。我們更容易感覺到一致的感念產生的美感,而不一致的概念往往很難令我欣賞。或許無名品質是我對一致的概念的一種模糊的審美吧。

我大抵能想到的就這么多,不知小tao是否滿意我的答卷。