全局函数序列(state:next:)和类型推断 [英] Global function sequence(state:next:) and type inference
问题描述
背景&详情
Swift 进化提案 SE-0094 是在 Swift 3.0 中实现的,引入了全局 sequence
函数:
后者声明如下
<块引用>func sequence(state: State,下一个:@escaping (inout State) ->T?)->展开序列<T,状态>
并在 swift/stdlib/public 中实现/core/UnfoldSequence.swift.语言参考给出了以下使用示例(注意缺少显式类型注释)
<块引用>//交织两个产生相同元素类型的序列序列(状态:(假,seq1.makeIterator(),seq2.makeIterator()),下一个:{迭代iters.0 = !iters.0返回 iters.0 ?iters.1.next() : iters.2.next()})
然而,我不能让上面的例子工作(例如使用let seq1 = 1...3
,let seq2 = 4...6
),但提示相当奇怪的错误消息
错误:对成员'sequence(first:next:)
'的不明确引用
仅当我在 next
闭包中显式注释可变的 State
参数及其返回类型时,上面的示例才能编译
让 seq1 = 1...3让 seq2 = 4...6for i in sequence(state: (false, seq1.makeIterator(), seq2.makeIterator()),next: { (iters: inout (Bool, ClosedRangeIterator, ClosedRangeIterator))->内部?在iters.0 = !iters.0返回 iters.0 ?iters.1.next() : iters.2.next()}) {打印(一)}//1 4 2 5 3 6
这不是我希望使用 sequence(state:next:)
的方式,但是,因为我更愿意在类型推断正常工作的动态应用程序中看到它, 避免所有的明确性.
问题
- 我们是否打算使用具有上述显式类型注释的
sequence(first:next:)
函数?由于inout
参数关闭,这个函数是否有一些限制,或者我遗漏了什么?
这看起来像是两个问题的组合.
第一个是 Swift 目前不会在没有任何外部上下文的情况下推断多行闭包的类型.然而,这是预期的行为,正如 Apple 开发人员 Jordan Rose 在 SR-1570 的评论中所证实的那样一个>:
<块引用>这是正确的行为:Swift 不会从多语句闭包的主体中推断参数或返回类型.但诊断可能会好很多.
因此理论上,您只需要明确定义传递给 sequence()
的 next:
参数的闭包的返回类型,作为参数类型可以从外部上下文推断(即您传递给 state:
参数的类型):
让 seq1 = 1...3让 seq2 = 4...6让组合 = 序列(状态:(假,seq1.makeIterator(),seq2.makeIterator()),下一个:{ iters ->内部?在iters.0 = !iters.0返回 iters.0 ?iters.1.next() : iters.2.next()})
(现在可以在 Swift 3.1 中编译)
<小时>然而,这仍然无法编译——这是由于第二个问题,编译器无法推断 Swift 3 中 inout
闭包参数的类型(在斯威夫特 2).这是一个可疑的错误,已经提交(参见 SR-1976 & SR-1811).
因此,正如您在问题中所指出的,这意味着(非常不令人满意)您必须明确注释您传递给 next:
:
let combine = sequence(state: (false, seq1.makeIterator(), seq2.makeIterator()),next: { (iters: inout (Bool, ClosedRangeIterator, ClosedRangeIterator)) ->内部?在iters.0 = !iters.0返回 iters.0 ?iters.1.next() : iters.2.next()})
Background & details
Swift evolution proposal SE-0094 was implemented in Swift 3.0, introducing the global sequence
functions:
The latter is declared as follows
func sequence<T, State>(state: State, next: @escaping (inout State) -> T?) -> UnfoldSequence<T, State>
and is implemented in swift/stdlib/public/core/UnfoldSequence.swift. The language reference gives the following example for using it (note the lack of explicit type annotation)
// 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() })
I cannot, however, get the example above to work (e.g. using let seq1 = 1...3
, let seq2 = 4...6
), but is prompted with the rather curious error message
error: ambiguous reference to member '
sequence(first:next:)
'
Only if I explicitly type annotate the mutable State
parameter in the next
closure, as well as the return type of it, does the example above compile
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
This is not the way I hope to use sequence(state:next:)
, however, as I'd rather see it in on-the-fly applications where type inference works as it should, avoiding all the explicitness.
Question
- Are we intended to use the
sequence(first:next:)
function with explicit type annotations as above? Is there some limitation in this function due to theinout
parameter closure, or am I missing something?
This looks like a combination of two issues.
The first is that Swift currently doesn't infer the type of a multi-line closure without any external context. This is however intended behaviour, as confirmed by Apple developer Jordan Rose in the comments of 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.
Therefore in theory, you would just need to explicitly define the return type of the closure you pass to sequence()
's next:
parameter, as the parameter type can be inferred from external context (namely the type you pass into the state:
parameter):
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()
})
(Edit: This now compiles in Swift 3.1)
However, this still doesn't compile – which is due to the second issue, where the compiler cannot infer the type for an inout
closure parameter in Swift 3 (which wasn't the case in Swift 2). This is a suspected bug, which has already been filed (see both SR-1976 & SR-1811).
Therefore, as you note in the question, this means (quite unsatisfactorily) that you have to explicitly annotate the full closure signature that you pass to 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()
})
这篇关于全局函数序列(state:next:)和类型推断的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!