New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[SR-10270] Lazy compact map invokes twice on last element #52670
Comments
Sorry, this is also correct. The lazy sequences in Swift do not cache their results, as a deliberate design choice. cc @lorentey again |
Comment by David Plunkett (JIRA) Thank you. I had a unit test that checked that a lazy operation was not applied any more times than it "needed" to be. The test passed prior to Xcode 10.2, but I guess the premise of my test was presumptuous. |
Comment by Alex Johnson (JIRA) @belkadan Are you sure this is the expected behavior? My teammate ran into this today, and we came up with the following example. Consider this lazy collection with five chained let array = ["cat", "dog", "fish"]
let lazyArray = array.lazy
.compactMap { i -> String? in
print("AAA: \(i)")
return i as String?
}
.compactMap { i -> String? in
print("BBB: \(i)")
return i as String?
}
.compactMap { i -> String? in
print("CCC: \(i)")
return i as String?
}
.compactMap { i -> String? in
print("DDD: \(i)")
return i as String?
}
.compactMap { i -> String? in
print("EEE: \(i)")
return i as String?
} Each transform just returns the input value, so if we convert it back to an array, we can see the each transform being executed for each element: Array(lazyArray) // => ["cat", "dog", "fish"] Output from AAA: cat
BBB: cat
CCC: cat
DDD: cat
EEE: cat
AAA: dog
BBB: dog
CCC: dog
DDD: dog
EEE: dog
AAA: fish
BBB: fish
CCC: fish
DDD: fish
EEE: fish We can also access an element by subscript and see each transform being executed once for that element: lazyArray[0] // => "cat" Output from AAA: cat
BBB: cat
CCC: cat
DDD: cat
EEE: cat And we can use an iterator: var i = lazyArray.makeIterator()
i.next() // => Optional("cat") Output from AAA: cat
BBB: cat
CCC: cat
DDD: cat
EEE: cat But, things start to get weird if we ask for the first element instead. There appears to be a diminishing loop where each transform is executed in a stacked manner. lazyArray.first // => Optional("cat") Output from AAA: cat
AAA: cat
BBB: cat
AAA: cat
BBB: cat
CCC: cat
AAA: cat
BBB: cat
CCC: cat
DDD: cat
AAA: cat
BBB: cat
CCC: cat
DDD: cat
EEE: cat
AAA: cat
BBB: cat
CCC: cat
DDD: cat
EEE: cat There are similar execution patterns when accessing other properties of the lazy collection, like |
Comment by David Plunkett (JIRA) It is pretty confusing - I didn't really understand the explanation that the results are not cached, but I figured it was just over my head. It would be cool to maybe find out some more about this behavior. |
Comment by Vincent Isambart (JIRA) Couldn't we for example add a specialization of var first: Element? {
var iterator = makeIterator()
return iterator.next()
} |
Environment
Xcode 10.2 (10E125)
Swift 5
Additional Detail from JIRA
md5: 6fa83fba79de904db5da8b327a2313a5
Issue Description:
Printing the input of each invocation of the closure demonstrates that it is invoked twice on the last mapped value when the mapping is applied lazily.
Expected: The printed values are -1, 0, 1
Actual: The printed values are -1, 0, 1, 1
The text was updated successfully, but these errors were encountered: