Skip to content
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-12928] .first is less efficient than .first { _ in true } on LazyMapSequence #55374

Open
swift-ci opened this issue Jun 2, 2020 · 3 comments
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. standard library Area: Standard library umbrella

Comments

@swift-ci
Copy link
Collaborator

swift-ci commented Jun 2, 2020

Previous ID SR-12928
Radar None
Original Reporter bubski (JIRA User)
Type Bug

Attachment: Download

Environment

Apple Swift version 5.2.2 (swiftlang-1103.0.32.6 clang-1103.0.32.51)
Target: x86_64-apple-darwin19.4.0

macOS 10.15.4 (19E287)

Xcode 11.4.1

Additional Detail from JIRA
Votes 0
Component/s Standard Library
Labels Bug
Assignee None
Priority Medium

md5: 38b32ecf66be259b2052152057060ea8

Issue Description:

_ = (1...1)
    .lazy
    .compactMap { value -> String? in
        print("lazy compactMap a \(value)")
        return "\(value)"
}.first

_ = (1...1)
    .lazy
    .compactMap { value -> String? in
        print("lazy compactMap b \(value)")
        return "\(value)"
}.first { _ in true }

_ = (1...1)
    .compactMap { value -> String? in
        print("eager compactMap \(value)")
        return "\(value)"
}.first

// output:
// lazy compactMap a 1
// lazy compactMap a 1
// lazy compactMap b 1
// eager compactMap 1

Shouldn't .first be synonymous to .first { _ in true } in terms of not only result, but also effort?

Also, is lazy

_ = (1...1)
    .lazy
    .compactMap { value -> String? in
        print("lazy compactMap a \(value)")
        return "\(value)"
}.first

being less efficient than eager

_ = (1...1)
    .compactMap { value -> String? in
        print("eager compactMap \(value)")
        return "\(value)"
}.first

expected?

@swift-ci
Copy link
Collaborator Author

Comment by Jarek Pendowski (JIRA)

Any updates on that? I know - pandemic and all that - but that's quite serious. Why is it evaluating `compactMap` twice?

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
NachoSoto added a commit to RevenueCat/purchases-ios that referenced this issue Nov 20, 2023
See apple/swift#55374

`.first` has been broken since 2020.
@NachoSoto
Copy link
Contributor

This is still an issue on Swift 5.9

NachoSoto added a commit to RevenueCat/purchases-ios that referenced this issue Dec 6, 2023
See apple/swift#55374

`.first` has been broken since 2020.
@oscbyspro
Copy link
Contributor

oscbyspro commented Feb 18, 2024

I looked into this and thought I'd share some insights. The lazy.compactMap({ $0 }).first expression invokes its closure twice as a result of how Collection/first, LazyFilterSequence/startIndex, and LazyMapSequence/subscript(_:) work:

@inlinable // lazy-performance
public func compactMap<ElementOfResult>(
_ transform: @escaping (Elements.Element) -> ElementOfResult?
) -> LazyMapSequence<
LazyFilterSequence<
LazyMapSequence<Elements, ElementOfResult?>>,
ElementOfResult
> {
return self.map(transform).filter { $0 != nil }.map { $0! }
}
}

@inlinable
public var first: Element? {
let start = startIndex
if start != endIndex { return self[start] }
else { return nil }
}

@inlinable // lazy-performance
public var startIndex: Index {
var index = _base.startIndex
while index != _base.endIndex && !_predicate(_base[index]) {
_base.formIndex(after: &index)
}
return index
}

@inlinable
public subscript(position: Base.Index) -> Element {
return _transform(_base[position])
}

As you can see, LazyFilterSequence invokes the closure while finding the startIndex, and LazyMapSequence invokes the closure again when the startIndex subscripts the collection. It is a bit silly. first { _ in true }, on the other hand, returns the first iterator element that passes through the filter.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. standard library Area: Standard library umbrella
Projects
None yet
Development

No branches or pull requests

3 participants