全局函数序列(state:next:)和类型推断 [英] Global function sequence(state:next:) and type inference

查看:22
本文介绍了全局函数序列(state:next:)和类型推断的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

背景&详情

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...3let 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 the inout 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆