斯威夫特是在三个月前宣布的。对我们许多人来说,这是我们职业生涯中最令人震惊和激动的事件之一。在这中间的几个月里,可以肯定地说,我们对这门语言的集体理解和欣赏已经发生了重大变化。
首先是迷恋期。我们关注的是外观,像Unicode支持(让🐶🐮!)及其新的简化语法。见鬼,甚至它的名字客观上也比它的前身好。
然而,在有机会阅读了几次Swift手册后,几周内,我们开始理解这种新的多范式语言的全部含义。所有那些为了听起来更聪明而影响函数程序员热情的人(泛型!)都学到了足够多的东西,开始备份它。我们终于区分了类和结构,并在此过程中学会了一些技巧,如**定制运算符和文字转换**。所有最初的兴奋现在都可以有效地引导到应用程序、库和教程中。
下周的公告实际上标志着iOS和OS X开发者夏季的结束。是时候控制我们的实验并再次开始发货了。
但是嘿,在事情再次成为现实之前,我们还有几天时间。让我们再学几件事:
泛型是Swift的决定性特征。与该语言强大的类型系统相协调,开发人员可以编写比目标C更安全、性能更强的代码。
泛型的基本机制是协议。Swift协议,就像目标C**@协议**一样,声明要实现的方法和属性以符合它。
在面向对象的范式中,类型经常与类标识混为一谈。然而**,在使用Swift编程时,在诉诸继承之前,首先考虑通过协议实现多态**。
协议的一个主要缺点,无论是在Swift还是目标C中,都是缺乏一种内置的方式来为方法提供默认实现,就像在其他语言中使用**混合或特征**一样。
……但这还不是故事的结尾。斯威夫特比最初表现出来的更**面向方面**。
考虑整个标准库中使用的均衡协议:
protocol Equatable { static func ==(lhs: Self, rhs: Self) -> Bool}
给定一个带有标题和正文字段的文章结构,实现赤道很简单:
struct Article { let title: String let body: String}extension Article: Equatable {}func ==(lhs: Article, rhs: Article) -> Bool { return lhs.title == rhs.title && lhs.body == rhs.body}
一切就绪后,让我们展示赤道的行动:
let title = "Swift Custom Operators: Syntactic Sugar or Menace to Society?"let body = "..."let a = Article(title: title, body: body)let b = Article(title: title, body: body)a == b // truea != b // false
等等… ! = 哪里来的?
!=不是由赤道协议定义的,它当然也不是为文章实现的。那么发生了什么?
!=实际上是从标准库中的这个函数中提取它的实现:
func !=<T : Equatable>(lhs: T, rhs: T) -> Bool
因为 ! = 被实现为等式的泛型函数,所以符合等式的任何类型,包括文章,也会自动获得 ! = 运算符。
如果我们真的想,我们可以推翻 ! =:的实施
func !=(lhs: Article, rhs: Article) -> Bool { return !(lhs == rhs)}
对于相等,我们不太可能提供比否定提供的==检查更有效的东西,但这在其他情况下可能是有意义的。斯威夫特的类型推理系统允许更具体的声明胜过任何泛型或隐式候选。
标准库在所有地方使用通用运算符,如按位操作:
protocol BitwiseOperationsType { func &(_: Self, _: Self) -> Self func |(_: Self, _: Self) -> Self func ^(_: Self, _: Self) -> Self prefix func ~(_: Self) -> Self class var allZeros: Self { get }}
以这种方式实现功能显著减少了在现有基础结构上构建所需的样板代码量。
方法的默认实现
然而,上述技术只对操作员有效。提供协议方法的默认实现不太方便。
考虑一个协议P,其方法m()采用一个Int参数:
protocol P { func m(arg: Int)}
最接近默认实现的方法是提供一个顶层泛型函数,该函数将显式Self作为第一个参数:
protocol P { func m() /* { f(self) }*/}func f<T: P>(_ arg: T) { …}
协议中注释掉的代码有助于将提供的功能实现传达给使用者。
所有这些都突出了Swift中方法和功能之间的显著紧张关系。
面向对象范例基于封装状态和行为的对象概念。然而,在Swift中,根本不可能在结构或类本身上实现某些泛型函数作为方法。
举个例子,包含方法:
func contains<S : SequenceType where S.Generator.Element : Equatable>(seq: S, x: S.Generator.Element) -> Bool
由于对序列生成器的元素是等式的约束,这不能在泛型容器上声明,而不要求该集合中的元素符合等式。
将包含、前进或分区等行为降级到顶级函数会对标准库造成损害。它不仅隐藏方法自动完成的功能,而且会在面向对象和函数范式中分割应用编程接口。
尽管这不太可能在1.0之前得到解决(当然还有更紧迫的问题),但有很多方法可以解决:
-
提供混合或特征功能,扩展协议以允许它们提供默认实现。
-
允许使用泛型参数进行扩展,这样像扩展Array<T: Equable>这样的东西可以定义其他方法,比如func包含(x: T),这些方法只对匹配特定条件的关联类型可用。
-
自动桥接函数调用,将Self作为使用隐式Self的方法可用的第一个参数。