全局函数 sequence(state:next:) 和类型推断

Global function sequence(state:next:) and type inference

背景和细节

Swift进化提案SE-0094在Swift3.0实现,引入全局sequence功能:

后者声明如下

func sequence<T, State>(state: State, 
                        next: @escaping (inout State) -> T?) ->
         UnfoldSequence<T, State>

并在 swift/stdlib/public/core/UnfoldSequence.swift 中实施。语言参考给出了以下使用示例(注意缺少显式类型注释)

// Interleave two sequences that yield the same element type
sequence(state: (false, seq1.makeIterator(), seq2.makeIterator()), next: { iters in
  iters.0 = !iters.0
  return iters.0 ? iters.1.next() : iters.2.next()
})

但是,我无法使上面的示例正常工作(例如使用 let seq1 = 1...3let seq2 = 4...6),但系统会提示我相当奇怪的错误消息

error: ambiguous reference to member 'sequence(first:next:)'

只有当我在 next 闭包中明确键入注释可变 State 参数,以及它的 return 类型时,上面的示例才会编译

let seq1 = 1...3
let seq2 = 4...6

for i in sequence(state: (false, seq1.makeIterator(), seq2.makeIterator()), 
        next: { (iters: inout (Bool, ClosedRangeIterator<Int>, ClosedRangeIterator<Int>)) 
                        -> Int? in
    iters.0 = !iters.0
    return iters.0 ? iters.1.next() : iters.2.next()
}) {
    print(i)
} // 1 4 2 5 3 6

然而,这不是我希望使用的方式 sequence(state:next:),因为我更愿意在动态应用程序中看到它,类型推断可以正常工作,避免所有显式。

问题

这看起来像是两个问题的结合。

首先,Swift 目前无法在没有任何外部上下文的情况下推断多行闭包的类型。然而,正如 Apple 开发人员 Jordan Rose 在 SR-1570:

的评论中所证实的那样,这是有意为之的行为

This is correct behavior: Swift does not infer parameter or return types from the bodies of multi-statement closures. But the diagnostic could be a lot better.

因此理论上,您只需要显式定义传递给 sequence()next: 参数的闭包的 return 类型,因为可以推断出参数类型来自外部上下文(即您传递给 state: 参数的类型):

let seq1 = 1...3
let seq2 = 4...6

let combined = sequence(state: (false, seq1.makeIterator(), seq2.makeIterator()),
                        next: { iters -> Int? in
    iters.0 = !iters.0
    return iters.0 ? iters.1.next() : iters.2.next()
})

(编辑:现在可以在 Swift 3.1 中编译)


然而,这仍然无法编译——这是由于第二个问题,编译器无法推断 Swift 3 中 inout 闭包参数的类型(这不是Swift 2) 中的案例。这是一个疑似错误,已提交(参见两者 SR-1976 & SR-1811)。

因此,正如您在问题中指出的那样,这意味着(非常不满意)您必须显式注释传递给 next::

的完整闭包签名
let combined = sequence(state: (false, seq1.makeIterator(), seq2.makeIterator()),
                        next: { (iters: inout (Bool, ClosedRangeIterator<Int>, ClosedRangeIterator<Int>)) -> Int? in
    iters.0 = !iters.0
    return iters.0 ? iters.1.next() : iters.2.next()
})

但即使承认这是一个错误,您仍然可以使代码 看起来 好得多,而不是担心它(在浏览器中输入而无需测试,但类似于这应该有效):

typealias MyTriple = (Bool, ClosedRangeIterator<Int>, ClosedRangeIterator<Int>)
let someTriple : MyTriple = (false, seq1.makeIterator(), seq2.makeIterator())
let combined = sequence(state: someTriple) { 
    (iters: inout MyTriple) -> Int? in
    iters.0 = !iters.0
    return iters.0 ? iters.1.next() : iters.2.next()
}