转载

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 能够早点招募真正的语言设计专家,采纳他们的建议。

原文  http://www.yinwang.org/blog-cn/2016/06/06/swift
正文到此结束
Loading...