这似乎是编译器基于下游操作 Select [a, b].compactMap
类型的结果.通过判断参数通过函数时的类型,可以看到这一点:
func printType<T>(_ i: String, _ v: T) -> T {
print(i, "->", T.self)
return v
}
func test() {
let a: String? = nil
let b: String? = nil
let foo: (String?) -> Int = { $0 == nil ? 0 : 1 }
let res1 = printType("cm1", printType("a1", [a, b]).compactMap { $0 }).map(foo)
let res2 = printType("cm2", printType("a2", [a, b]).compactMap { $0 }).map { foo($0) }
print("res1: ", res1)
print("res2: ", res2)
}
test()
// a1 -> Array<Optional<Optional<String>>>
// cm1 -> Array<Optional<String>>
// a2 -> Array<Optional<String>>
// cm2 -> Array<String>
// res1: [0, 0]
// res2: []
看来:
- In the case of
res1
:
- 因为
foo
接受String
,所以map(foo)
被输入,这样String?
就被通过了——而map
要接收String?
,compactMap
必须返回[String?]
- 为了让
compactMap
返回[String?]
,它的输入必须是[String??]
- 虽然
[a, b]
默认为[String?]
,但编译器也可以隐式地将其upcast转换为[String??]
,所以它就是这么做的
- 因此,仅从
[String??]
中移除一层可选性,并且String?
个值中的每一个被传递到map
并进入foo
- In the case of
res2
, the compiler isn't restricted as heavily by map { foo($0) }
, since $0
can be implicitly upcast inside of the closure before being passed to foo
, and this is what the compiler prefers:
$0
是一个String
,在传递到foo
之前是upcast到String?
map
要接收String
个值,compactMap
必须返回[String]
- 因为
compactMap
返回[String]
,所以它的输入必须是[String?]
,也就是[a, b]
,不需要向上投射
- 因此,现在
compactMap
实际上是将[String?]
中的nil
过滤成[String]
,因为没有留下任何值,所以foo
甚至从来没有被调用过(你可以看到foo
中有更多的打印语句)
这类事情在编译器中是很常见的,你碰巧发现了一个具体的例子.例如,编译器对单语句闭包结果的分析与对多语句闭包的分析不同:如果将compactMap { $0 }
转换为compactMap { _ = 1 + 1; return $0 }
,则闭包的分析将不同,类型判断将以不同的顺序进行,在这两种情况下都会产生[]
:
let res1 = printType("cm1", printType("a1", [a, b]).compactMap { _ = 1 + 1; return $0 }).map(foo)
let res2 = printType("cm2", printType("a2", [a, b]).compactMap { _ = 1 + 1; return $0 }).map { foo($0) }
// a1 -> Array<Optional<String>>
// cm1 -> Array<Optional<String>>
// a2 -> Array<Optional<String>>
// cm2 -> Array<Optional<String>>
// res1: [0, 0]
// res2: [0, 0]
在本例中,编译器实际上在两个实例中都更喜欢surprising,因为return $0
允许编译器从String?
向上转换➡ String??
(然后从[String??]
向下过滤➡ [String?]
)!
无论如何,这似乎值得一份关于https://bugs.swift.org的报告,因为这种行为令人难以置信地惊讶.如果您继续并归档,我将很高兴向报告中添加测试用例和注释.