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:
Therefore in theory, you would just need to explicitly define the return type of the closure you pass to
sequence()
'snext:
parameter, as the parameter type can be inferred from external context (namely the type you pass into thestate:
parameter):(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:
:But even admitting that this is a bug, you can still make the code look a lot nicer instead of worrying about it (typed in the browser without testing, but something like this should work):