Swift 語言的設計錯誤
在『編程的智慧』一文中,我表揚了 Swift 語言的 option type 設計,但這并不等于 Swift 的設計是***沒有問題的。其實 Swift 1.0 剛出來的時候,我就發現它的 array 可變性設計存在嚴重的錯誤。Swift 2.0 修正了這個問題,然而他們的修正方法卻仍然是錯誤的。這個錯誤一直延續到今天。
Swift 1.0 試圖利用 var 和 let 的區別來指定 array 成員的可變性,然而其實 var 和 let 只能指定 array reference 的可變性,而不能指定 array 成員的可變性。舉個例子,Swift 1.0 試圖實現這樣的語義:
var shoppingList = ["Eggs", "Milk"]
shoppingList[0] = "Salad" // 可以對 array 成員賦值
let shoppingList = ["Eggs", "Milk"]
shoppingList[0] = "Salad" // 不能對 array 成員賦值,報錯
然而這是錯誤的。為了理解這個問題,首先你應該清晰地區分 array reference 和 array 成員的區別。在這個例子里, shoppingList 是一個 array reference,而shoppingList[x] 這樣的形式,是訪問一個 array 成員。因為 var 和 let 本來是用于指定 shoppingList 這個 reference 是否可變,也就是決定 shoppingList 是否可以指向另一個 array。它并不能用于指定 array 成員的可變性。正確的用法應該是這樣:
var shoppingList = ["Eggs", "Milk"]
shoppingList = ["Salad", "Noodles"] // 可以對 array reference 賦值
let shoppingList = ["Eggs", "Milk"]
shoppingList = ["Salad", "Noodles"] // 不能對 array reference 賦值,報錯
var 和 let 只能有一個用途。它們一旦被用于指定 reference 的可變性,就不再能用于指定 array 成員的可變性。如果你理解 reference 是放在棧(stack)上的,而 Swift 1.0 的 array 是放在堆(heap)上的,就會明白 var 和 let 其實只能指定棧上數據的可變性。堆上的數據可變性,必須用另外的方式來指定。
實際上很多語言都已經看清楚了這個問題。C++ 程序員都知道 int const * 和int * const 的區別。Objective C 程序員都知道 NSArray 和 NSMutableArray 的區別。我不知道為什么 Swift 的設計者看不到這個問題。
Swift 2.0 修正了這個問題,可是它的修正方法卻是錯誤的:它把 array 從 reference type 變成了所謂 value type,也就是說把 array 放在棧上,而不是堆上。這貌似解決了以上的問題,因為由于 array 是棧數據, shoppin List 不再是一個 reference,而是代表 array 的數據本身,所以你確實可以用 var 和 let 來決定它是否可變。
這看似一個可行的解決方案,然而它卻沒有擊中要害,它帶來了另外的問題。把 array 作為 value type,使得每一次對 array 的賦值或者參數傳遞,都必須進行拷貝。你沒法使兩個變量指向同一個 array,也就是說 array 不再能被共享。
這違反了程序員對于數組這種大型結構的“心理模型”,他們不再能清晰方便的對 array 進行思考,容易犯錯誤。而且對數組的拷貝需要大量時間,對效率有很大影響。沒有任何另外的現代語言(Java,C#,……)把 array 作為 value type。
實際上如果你看透了它的實質,就會發現 value type 這東西的存在,其實是沒有意義的。眾多的新語言,Swift,Rust 試圖利用 value type 之類來解決內存管理的效率問題,然而它帶來的好處是微乎其微的,給程序員帶來的麻煩和困擾卻是有目共睹的。
從 Swift 的設計犯下這樣的低級錯誤,你也許可以看出來,編譯器專家并不等于程序語言專家。很多程序語言專家一看到 Swift 最初的 array 設計,就知道那是錯的。為什么 Swift 的設計者直到 1.0 發布都沒有發現,到了 2.0 修正卻仍然是錯誤的呢?我猜這是因為 Apple 并沒有聘請合格的程序語言專家進行 Swift 的設計。Swift 的***設計師是 Chris Latner,也就是 LLVM 的設計者。Chris Latner 是一個不錯的編譯器專家,然而他卻只能算是業余級別的程序語言設計師。編譯器和程序語言,真的是兩個非常不同的領域,不要盲目的以為好的編譯器作者就能設計出好的程序語言。
由于沒有招募到深入精髓的設計師,自己瞎倒騰,使得 Swift 語言團隊犯下根本不該犯的錯誤。***次就應該做對的事情,卻需要經過很多次的返工。以至于每出一個新的版本,就出現很多“breaking change”,導致之前版本的 Swift 代碼不再能用。這種情況對于 Swift 語言并不是世界末日,然而我希望 Apple 能夠早點招募真正的語言設計專家,采納他們的建議。