这是Swift 3.0.2中flatMap的合约
public struct Array<Element> : RandomAccessCollection, MutableCollection {
public func flatMap<ElementOfResult>(_ transform: (Element) throws -> ElementOfResult?) rethrows -> [ElementOfResult]
}
如果我取一个[String?]
flatMap数组返回[String]
let albums = ["Fearless", nil, "Speak Now", nil, "Red"]
let result = albums.flatMap { $0 }
type(of: result)
// Array<String>.Type
此处ElementOfResult
变为String
,为什么不String?
?泛型类型系统如何从表达式中删除Optional部分?
答案 0 :(得分:4)
当您使用标识转换{ $0 }
时,编译器将推断ElementOfResult?
(转换的结果)等效于Element
(转换的参数)。在这种情况下,Element
为String?
,因此ElementOfResult?
== String?
。这里不需要可选的促销,因此ElementOfResult
可以推断为String
。
因此,flatMap(_:)
在这种情况下会返回[String]
。
在内部,从闭包返回ElementOfResult?
到ElementOfResult
的这种转换只需通过有条件地解包可选项来完成,如果成功,则将未包装的值附加到结果中。您可以看到exact implementation here。
作为附录,请注意as Martin points out,闭包只有在单一陈述闭包时才会参与类型推断(请参阅this related bug report)。乔丹·罗斯in this mailing list discussion提出了这个原因:
Swift的类型推断目前是面向语句的,因此没有简单的方法来进行[多语句闭包]推理。这至少部分是编译时间问题:Swift的类型系统允许比Haskell或OCaml更多可能的转换,因此解决整个多语句函数的类型不是一个小问题,可能不是一个易处理的问题。
这意味着对于具有多个语句的闭包,这些语句传递给map(_:)
或flatMap(_:)
等方法(其中结果类型是通用占位符),您必须显式注释返回类型闭包,或方法返回。
例如,这不会编译:
// error: Unable to infer complex closure return type; add explicit type to disambiguate.
let result = albums.flatMap {
print($0 as Any)
return $0
}
但这些确实:
// explicitly annotate [ElementOfResult] to be [String] – thus ElementOfResult == String.
let result: [String] = albums.flatMap {
print($0 as Any)
return $0
}
的
// explicitly annotate ElementOfResult? to be String? – thus ElementOfResult == String.
let result = albums.flatMap { element -> String? in
print(element as Any)
return element
}