I don't quite understand how lazy sequences interact with type inference. What's going on here?
func f1(_ array: [Int]) -> [Int] {
array.lazy
.map { print("map triple: \($0)"); return $0 * 3 }
.filter { print("filter even: \($0)"); return $0 % 2 == 0 }
.map { print("map double: \($0)"); return $0 * 2 }
}
print("f1 - WHY?")
let r1 = f1(Array(1...2))
print(r1)
This prints:
f1 - WHY?
map triple: 1
filter even: 3
map triple: 2
filter even: 6
map triple: 1
filter even: 3
map triple: 2
filter even: 6
map triple: 2
map double: 6
[12]
Clearly, some of the steps are executed multiple times. The issue appears to be the way in which the resulting lazy sequence is converted into [Int]. Manual conversion to Array fixes the problem:
func f2(_ array: [Int]) -> [Int] {
Array(array.lazy
.map { print("map triple: \($0)"); return $0 * 3 }
.filter { print("filter even: \($0)"); return $0 % 2 == 0 }
.map { print("map double: \($0)"); return $0 * 2 })
}
print("\nf2 - manual conversion to Array")
let r2 = f2(Array(1...2))
print(r2)
Now the output is as expected:
f2 - manual conversion to Array
map triple: 1
filter even: 3
map triple: 2
filter even: 6
map double: 6
[12]
Surprisingly, another way to fix this is to pass a Sequence to the function rather than an Array:
func f3<S: Sequence<Int>>(_ seq: S) -> [Int] {
seq.lazy
.map { print("map triple: \($0)"); return $0 * 3 }
.filter { print("filter even: \($0)"); return $0 % 2 == 0 }
.map { print("map double: \($0)"); return $0 * 2 }
}
print("\nf3 - Sequence as input")
let r3 = f3((1...2))
print(r3)
Again, the output is as expected:
f3 - Sequence as input
map triple: 1
filter even: 3
map triple: 2
filter even: 6
map double: 6
[12]
I understand that lazy sequences don't cache their results which is why some steps may get executed repeatedly. What I don't really understand is how type inference, type conversions and input types interact in these examples. This seems pretty hard to reason about and in fact makes me never want to touch lazy sequences with a bargepole.