Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
607 views
in Technique[技术] by (71.8m points)

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

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?
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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()
})

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...