When lazily mapping an array of values, I receive an instance of type LazyMapSequence as expected:
Welcome to Apple Swift version 5.7 (swiftlang-5.7.0.127.4 clang-1400.0.29.50).
Type :help for assistance.
1> let numbers = Array(1...5)
numbers: [Int] = 5 values {
[0] = 1
[1] = 2
[2] = 3
[3] = 4
[4] = 5
}
2> let squares = numbers.lazy.map { $0 * $0 }
squares: LazyMapSequence<LazySequence<[Int]>.Elements, Int> = {
_base = 5 values {
[0] = 1
[1] = 2
[2] = 3
[3] = 4
[4] = 5
}
_transform =
}
However, if the map(_:) method receives a throwing a closure instead, the mapping is not performed lazily, and I receive an array instead:
3> func square(_ x: Int) throws -> Int {
4. return x * x
5. }
6> let squares = try numbers.lazy.map(square)
squares: [Int] = 5 values {
[0] = 1
[1] = 4
[2] = 9
[3] = 16
[4] = 25
}
Why is that, and how do I lazily map an array of values using a throwing closure?
A workaround is
Note that the element type of the sequence is
Result<U, Error>. We essentially "catch" the error whenever any is thrown. The error has to be caught because when iterating over anySequence, the protocol requires that no errors are thrown.As for why
map(square)is not lazy, it is exactly as you have observed.LazySequenceProtocol.maptakes a closure that does not throw.When you pass in the throwing method, it instead calls
Sequence.map:which is not lazy.
This would all be solved if there were a method that looked like:
However, such a
LazyThrowingMapSequencetype cannot conform toSequence, because its iterator cannot conform toIteratorProtocol. Its iterator'snextmethod throws, butIteratorProtocolrequires thatnextdoes not throw.It is theoretically possible to write
LazyThrowingMapSequenceby just addingthrowsto a few places toLazyMapSequence. (Source code ofLazyMapSequenceis here) But using it would be a pain, since you cannot iterate over it with aforloop, and it doesn't have any of those convenient methods from theSequenceprotocol.