做者:Soroush Khanlou,原文连接,原文日期:2016-08-14
译者:Joy;校对:冬瓜;定稿:CMBphp
在之前,我已经写过不少喜欢 Swift 的理由。可是今天,我想要写的是这门语言不足的地方。这是一个锱铢必较的问题,因此我将举例描述,去指出这门语言作的好的地方,作的很差的地方,以及其前景。git
看一下 Ruby
语言的状况github
Ruby
的 attr_accessor
是一种为实例变量定义 setter
和 getter
的方法。你能够像下面这样使用它objective-c
class Person attr_accessor :first_name, :last_name end
乍一看,它像是一种语言特性,与 Swift
的 let
和 var
属性声明方式类似。可是, Ruby 的函数即使没有括号也能够被调起,并且这只是一个被定义在类范围内的函数(在 Swift 中咱们将会调起一个静态函数):编程
def self.attr_accessor(*names) names.each do |name| define_method(name) {instance_variable_get("@#{name}")} # 这是 getter 方法 define_method("#{name}=") {|arg| instance_variable_set("@#{name}", arg)} # This is the setter end end
若是你不能读懂 Ruby
,没有关系。它使用了一个名为 define_method
的函数来为你传递的变量建立一个 getter
和 setter
方法。在 Ruby
中,@first_name
意味着一个名为 first_name
的实例变量。json
这是我爱上 Ruby
这门语言的缘由之一 ,他们首先设计了元数据工具集,去建立有用的语言特性,而后使用这些工具去实现他们想要的语言特性。 Yehuda Katz explores讲述了 Ruby
是如何在它的 blocks
中实现这一想法的。由于 Ruby 的语言特性是经过相同的工具和相同的语言编写而成,而且这门语言全部用户都有权使用,因此,在这门语言的范畴内和类似风格的状况下,用户也能够编写语言特性。swift
Swift 的一个核心特性就是它的可选类型。它容许用户定义某个变量是否能够为空。在系统中,被定义为枚举的格式:promise
enum Optional<WrappedType> { case Some(WrappedType) case None }
就像 attr_accessor
,这个特性使用了一个 Swift
的语言结构来定义自身。这很不错,由于这意味着用户可使用不一样的语义来建立类似的事物,就像这个虚构的 RemoteLoading
类型:ruby
enum RemoteLoading<WrappedType> { case Loaded(WrappedType) case Pending }
它和 Optional
(可选类型)有相同的形态,但有着不一样的含义。(在这篇博文中,Arkadiusz Holko 曾对这个枚举有更进一步的阐述)数据结构
然而,在某种程度上,Swift
的编译器知道 Optional
(可选) 类型但殊不知道 RemoteLoading
(远程加载),它可让你作一些特殊的事情。看一下这些相同的声明:
let name: Optional<String> = .None let name: Optional<String> = nil let name: String? = nil var name: String?
让咱们解析下它们的含义。第一条语句是完整的表述(带有类型推断)。你可使用相同的语法声明你本身的 RemoteLoading
(远程加载)属性。第二条语句使用了 NilLiteralConvertible
协议来定义当你把这个值设置为 nil
的时候所要执行的操做。虽然这种语法对于你本身的类型访问是能够的,可是使用 RemoteLoading
(远程加载)却显得不是很正确。这是第一个语言特性,使得 C
族语言开发者对Swift有更舒服的感受,待会咱们会再次提到这一点。
第三条和第四条语句,编译器开始使用 Optional
(可选) 类型来容许咱们编写特殊的代码。第三条语句使用了一个 Optional
(可选)类型的简写 T?。这被称为 语法糖,可让你使用简单的方式来编写经常使用的代码。最后一句是另一块语法糖:若是你定义一个可选类型,可是你不赋给它任何值,那编译器将会推测出它的值应该为 .None
/ nil
(仅仅当他是一个 var
变量的时候才成立)。
后面的两条语句不支持自定义类型。语言的 Optional 类型,能够经过语言内存在的结构定义,以特定类型的异常结束,这个异常只有当前类型能够访问。
Swift
被定义为“像在 C 语言家族中同样”的语言,这个得益于循环和 For
语句。
Swift
的 for..in
语法结构是特殊的。任何遵照 SequenceType 的数据结构,均可以经过一个 for..in 循环来遍历。这就意味着,我能够定义本身的类型值,并声明他们是序列化的,就能够用for..in 循环来遍历它了。
虽然 if 语句和 while 循环是经过 BooleanType 类型 在 Swift 2.2 中这样子工做的 ,可是这种功能在 Swift 3 已经被移除了。我不能像在 for..in 循环语句中那样子定义本身的布尔类型值而后在 if 语句中使用。
这里有两种基本的方法去定义语言特性,在 Swift 中都有体现到。第一种是建立一个能够用来定义语言特性的元工具;另外一种是定义语言特性和语言类型值之间的一种明确和具体的关系。
你能够会对符合 SequenceType 的类型值比符合 BooleanType 的类型值更加有用这个观点提出异议。可是,Swift 3 已经彻底的移除了这个特性,因此,你只能认可:你不得不去认为 BooleanType 是如此没有用处以致于会被彻底禁止。
在 Swift 中的运算符也值得研究。语言中存在着定义运算符的语法,全部的算术运算符都是在这个语法中被定义的。用户们能够自由的定义本身的运算符,若是你想要建立本身的 BigInt 类型,同时也想要使用标准的算术运算符,这将是很是有用的。
然而 +
运算符在语言中被定义,三元运算符 ?:
却没有。当你点击 +
时,命令跳转到这个运算符的声明处。当你点击三元运算符中的 ?
和 :
的时候,却没有任何反应。若是你想要在你的代码中使用单个的问号和感叹号做为操做符的话,这是作不到的。注意我这里 不是 说在你的代码中使用一个感叹号操做符不是一个好主意。我只是想说,这个操做符已经被特殊对待,硬编码到了编译器,与其余 C
中定义的操做符通常无二。
在这三个状况中,我都比较了两个东西:一是被标准库用来实现特性的有用语法,一种是特权标准库超越使用者代码的特殊状况。
最好的语法和语法糖是能够被一门语言的做者利用本身的类型和系统不断深刻挖掘的。Swift 有时使用NilLiteralConvertible
,SequenceType
和BooleanType
来处理这些状况。这种 var name: String?
可以推测出本身的默认属性值(.None)的方式很明显不符合这个条件,所以这是一种不那么给力的语法糖。
我认为另外一个值得注意的点是,即便我爱 Ruby 的语法,可是 Ruby 在运算符和 falsiness
这两个地方却不是很灵活。你能够自行定义已存在运算符的实现方式,可是不能添加一个新的运算符,并且运算符的优先级也是固定的。Swift 在这个方面更灵活。并且,固然,在 Swift 3 以前,Swift 在定义 falsiness
方面一样具备更强的灵活性。
在某种程度上来讲,Swift 的可选类型相似于 C 语言的可控性, Swift 的错误处理也相似于 C 语言的异常处理。Swift 的错误处理引入了一些新的关键词: do
, try
, throw
, throws
, rethrows
, 和 catch
。
使用 throws
标记的函数和方法能够 return
一个值或者 throw
一个 ErrorType
。被抛出的错误将会在 catch blocks函数中被捕捉到。在幕后,你能够想象到 Swift 经过可能表明成功或者失败的 _Result
类型(就像 antitypical/Result)重写了函数的返回值类型,例如:
func doThing(with: Property) throws -> Value
重写为
func doThing(withProperty) -> _Result<Value, ErrorType>
事实上,这种 _Result 类型并无被显式定义,而是 在编译器中被隐式的处理了 )。这对于咱们的例子并无形成太多的不一样。)在调用函数的内部,传入成功的值的时候将会经过 try 语句,而发生错误的时候,则会跳入并执行 catch block
函数。
对比这个和以前的例子,例子中语言特性被定义在语言内部,再加上语法(例如操做符和 SequenceType)和语法糖(例如 Optional),那么这个代码就变的像咱们所期待的那样了。相反的,Swift 的错误处理并无暴露它的内部 _Result 模型,因此用户没法使用或者改变它。
一些状况下使用 Swift 模型来进行错误处理很是合适,例如 Brad Larson 用来移动机器人手臂的代码和 个人 JSON 解析代码 。其余状况的话,使用 Result 类型和 flatMap 会更合适。
其余的代码可能依赖异步处理,并想要传递一个 Result 的类型值给completion block
。苹果的解决方案只能在某些特定的状况下起到做用,给予在错误模型上更大的自由能够帮助缩小这门语言和使用者之间的距离。Result 是很好的,由于它足够灵活,能够在上面玩不少花样。 try / catch 语法并非很给力,由于它的使用十分严格并且只有一种使用方法。
Swift 4 承诺在不久后,将使用异步的语言特性。目前还不清楚将如何实现这些功能,可是 Chris Lattner 已经写了不少关于 Swift 4的东西
一类并发:Actors、同步/等待、原子性、内存模型及其它一些相关主题
Swift 的异步的处理机制将会是什么样子的,异步/等待 是个人主要理论。在外行人看来,异步/等待 声明何时函数是异步的
async Task<int> GetIntAsync() { return new Task<int>(() => { Thread.Sleep(10000); return 1 }); } async Task MyMethodAsync() { int result = await GetIntAsync(); Console.WriteLine(result); }
第一个函数方法, GetIntAsync
返回了一个任务,该任务等待一段时间后返回了一个值。由于这个函数返回了一个 Task
,因此被标记为 async
。第二个函数方法,首先调用 MyMethodAsync
,使用关键词 await
。这通知了整个系统,在完成 GetIntAsync
任务以前,这个系统能够作其余的事情。而一旦这个任务完成了,这个函数就会恢复控制功能,并在控制台打印。
从这个例子看来,C#
的 Task
对象看起来很像 Promise 。此外,任何使用 await 的函数都必须被定义为 async 。编译器能够确保这点。这个解决方案与 Swift 的错误处理模型很类似:被抛出的函数方法必须被捕捉到,而若是没有,那这些函数方法必定也是被标记了 throws 。
它也像错误处理模型同样有着缺陷。在加上新构造和一些关键词以后,更像是语法糖,而不是一个有用的工具。这种构造一部分依赖于在标准库中定义的类型,一部分依赖于编译器定义的语法。
属性行为是 Swift 4 可能引入的另外一个重大特性。这里是关于属性行为的拒绝提案,在 Swift 4中,这一个特性被更密切的关注。
属性行为让你能够对一个属性附加上行为,好比添加 lazy。这个 lazy 属性,举个例子,只有它在被第一次访问时才设置值。但你如今已经可使用这个特定的行为,这是直接硬编码进 Swift 编译器的。属性行为将使标准库更容易地实现一些行为,同时方便用户去彻底自定义行为。
可能这已是全世界最好的特性了。从一个已经被硬编码进编译器的一个特性开始,而后在这个特性取得必定声望以后,建立一个更通用的框架来容许你经过语言自己定义这个特性。基于这一点,,任何 Swift 的开发者均可以实现相似的功能,精确调整来知足本身的需求。
若是 Swift 的错误模型遵循着相同的路径,Swift 的标准库可能会暴露出一个 Result 类型值,而后任何返回 Result 值的函数,均可以在必要的时候,使用 do
/ try
/ catch
语法(就像那些能够单个失败的并行、同步事件)。对于那些不须要符合当前可用语法的错误,就像异步错误,用户将可使用一个共同的 Result
。这个 Result
须要不少链,用户能够 flatMap
。
异步/等待能够按照一样的方式。定义一个 Promise
或 Task
协议,并遵照他们,那么任务将是能够等待的(await)。then
和 flatMap
在这里是可用的,根据用户的需求,能够来选择对应的语言特性。
我想要更多地去写一些关于元编程的知识。我已经写了关于 Objective-C 中的元编程,它和咱们正在着手作的事情很类似。代码和元代码之间的界限是模糊的。Swift 编译器中的代码是元代码,而且 Swift 自己也是代码。若是定义一个 operator
函数的实现(就像你使用Ruby同样)就是代码,那么定义一个全新的运算符看起来就像是元代码。
做为一种面向协议的语言,Swift 很独特,可让咱们挖掘这门语言的语法魅力,就像咱们用 BooleanType
和 SequenceType
所作的同样。我很乐意去看一下这些被扩展的能力。
关键词中止和语法开始或者语法中止和语法糖开始的界限,不是很明确,可是对于使用这门语言编写代码的工程师,应该有能力去使用那些开发标准库的工具。
本文由 SwiftGG 翻译组翻译,已经得到做者翻译受权,最新文章请访问 http://swift.gg。