羅素悖論 型別系統與程式語言

banq發表於2012-03-29
程式語言的基礎核心來自於邏輯,來自PROGRAMMING LANGUAGES & TYPE SYSTEMS文章從羅素悖論角度解釋,為什麼我們引入型別系統,然後才有了今天的程式語言,這對深入理解程式語言來源,破除語言誤區有很大幫助。

著名的羅素悖論是:一個集合到底包含不包含它自己?

舉個例子,如下集合a包含a本身:
a = { a, b }
但是,我們常識中對樹形結構的瞭解,一個節點(枝)是由其他節點組成的(左 右或子),但肯定不是由它自己組成的,因此我們又認為集合a不應該包含a本身:
a={任何除了a的元素}
或a={b}

我們總結下面:
1.集合包含他們自己
2.集合不包含他們自己

如果有很多集合,這些很多集合也可以表現為一個大集合,那麼我們得到如下描述:
1.所有集合的集合應該包含他們自己。
2.所有集合的集合不應該包含他們自己。

從前面推論我們已經知道,我們傾向於第二條為真,但是注意第二句就發生了邏輯矛盾,如果我們需要統計不包含自己的所有集合,必須首先統計其主集合,因為主集合實際上是包含了所有集合的,但是主集合也是一種集合,而集合是不應該包含他們自己的,結果這裡發生矛盾,這就是著名的羅素悖論。

解決羅素悖論是引入型別理論(http://en.wikipedia.org/wiki/Type_theory),引入不同型別的層次,每個層次結構中的層只是由同一型別中先前層次組成的。這就誕生了我們今天現代語言Java, C#, Ruby, Haskell 等等,都是採取型別理論,實現特定的屬性和層次。

知道這種來歷,對於理解程式語言中動態型別(Ruby)和靜態型別(Java Scala)等區別很有幫助,還有一種強型別和弱型別,比如Java如下:

// Given a method:
public void increment(int i);

// This call is illegal
increment(24.0);

增加這個方法是非法的,因為24.0是一種double型別,而方法函式引數型別要求是int,Java並不知道如何將24.0轉換成int型別,但是也不鼓勵由Java自動實現,因為Double在Java中是64位,而int是32位,進行自動轉換會丟失資訊。

但是Java允許 3+24.0這種演算法,這是一種型別擴充套件type-widening表達用法,算式的結果型別是double的,整數int能夠被保護,這種轉換將被允許,但是int x = 3+24.0又不允許。

在動態型別語言Python中"1"+2 結果是錯誤的:
TypeError: cannot concatenate 'str' and 'int' objects

告訴我們範了一個型別錯誤,Python並不知道如何轉換這些型別,同樣Ruby會報類似錯誤,這其實是一種強型別影響的表現,它存在在靜態型別語言Java C#,也存在動態型別語言Ruby Python Scheme中。

為了糾正這種錯誤,我們必須顯式地進行轉換,如Python必須如下:

"1" + str(2) # returns '12'

或者:
int("1") + 2 # returns 3

很顯然Python並不確定"+"(加號)這個運算子應當將字元轉為整數型,它留給程式設計師解決,這是強型別系統中一個最佳實踐。

在java中下面是允許的:
"1" + 2 // returns "12"

這裡"+"加號運算子被認為是呼叫物件的.toString(),這是值得商榷的選擇,但是它增強了靈活性。

在Python中,一個函式能夠接受任何型別的物件:

def fun(arg):
...

這比報型別錯誤要更加安全,相反Java支援強制性型別:
void fun(String arg) { .. }

這是一種更加安全的方式,只接受特定型別引數。

大部分動態型別語言都假設任何物件能夠被轉換成String,這是一種為了方便而實現的妥協。

下面談談弱型別,javascript是一種弱型別語言:
"100" > 10 // returns true

和上面談到的強型別不同的是,弱型別語言定義了一些基本約束,防止程式設計師射中自己的腳,這些型別的自動轉換是語言的特別之處,但是每個語言也有自己在遭遇矛盾型別情況下的解決方式:

100 + "1" + 0

結果將是"10010",而在VB,將是數字101,這種轉換雖然帶來方便,但是也有一定隨意性,這時強型別語言顯示出其特有的可預測的邏輯一致性了。

結論:
強型別Ruby和java對於團隊開發更加安全,而弱型別則可提供快速基於瀏覽器內部的開發 。









相關文章