全局函数 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...3
、let 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:)
,因为我更愿意在动态应用程序中看到它,类型推断可以正常工作,避免所有显式。
问题
- 我们是否打算像上面那样使用带有显式类型注释的
sequence(first:next:)
函数?由于 inout
参数闭包,此函数是否存在某些限制,或者我是否遗漏了什么?
这看起来像是两个问题的结合。
首先,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()
}
背景和细节
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...3
、let 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:)
,因为我更愿意在动态应用程序中看到它,类型推断可以正常工作,避免所有显式。
问题
- 我们是否打算像上面那样使用带有显式类型注释的
sequence(first:next:)
函数?由于inout
参数闭包,此函数是否存在某些限制,或者我是否遗漏了什么?
这看起来像是两个问题的结合。
首先,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()
}