|
| 1 | +# Compacted |
| 2 | + |
| 3 | +* Proposal: [NNNN](NNNN-compacted.md) |
| 4 | +* Authors: [Philippe Hausler](https://github.com/phausler) |
| 5 | +* Status: **Implemented** |
| 6 | + |
| 7 | +* Implementation: [Source](https://github.com/apple/swift-async-algorithms/blob/main/Sources/AsyncAlgorithms/AsyncCompactedSequence.swift) |
| 8 | + [Tests](https://github.com/apple/swift-async-algorithms/blob/main/Tests/AsyncAlgorithmsTests/TestCompacted.swift) |
| 9 | + |
| 10 | +## Proposed Solution |
| 11 | + |
| 12 | +Similar to the Swift Algorithms package we propose that a new method be added to `AsyncSequence` to fit this need. |
| 13 | + |
| 14 | +```swift |
| 15 | +extension AsyncSequence { |
| 16 | + public func compacted<Unwrapped>() -> AsyncCompactedSequence<Self, Unwrapped> |
| 17 | + where Element == Unwrapped? |
| 18 | +} |
| 19 | +``` |
| 20 | + |
| 21 | +This is equivalent to writing `.compactMap { $0 }` from a behavioral standpoint but is easier to reason about and is more efficient since it does not need to execute or store a closure. |
| 22 | + |
| 23 | +## Detailed Design |
| 24 | + |
| 25 | +The `AsyncCompactedSequence` type from an effects standpoint works just like `AsyncCompactMapSequence`. When the base asynchronous sequence throws, the iteration of `AsyncCompactedSequence` can throw. Likewise if the base does not throw then the iteration of `AsyncCompactedSequence` does not throw. This type is conditionally `Sendable` when the base, base element, and base iterator are `Sendable. |
| 26 | + |
| 27 | +```swift |
| 28 | +public struct AsyncCompactedSequence<Base: AsyncSequence, Element>: AsyncSequence |
| 29 | + where Base.Element == Element? { |
| 30 | + |
| 31 | + public struct Iterator: AsyncIteratorProtocol { |
| 32 | + public mutating func next() async rethrows -> Element? |
| 33 | + } |
| 34 | + |
| 35 | + public func makeAsyncIterator() -> Iterator { |
| 36 | + Iterator(base.makeAsyncIterator()) |
| 37 | + } |
| 38 | +} |
| 39 | + |
| 40 | +extension AsyncCompactedSequence: Sendable |
| 41 | + where |
| 42 | + Base: Sendable, Base.Element: Sendable, |
| 43 | + Base.AsyncIterator: Sendable { } |
| 44 | + |
| 45 | +extension AsyncCompactedSequence.Iterator: Sendable |
| 46 | + where |
| 47 | + Base: Sendable, Base.Element: Sendable, |
| 48 | + Base.AsyncIterator: Sendable { } |
| 49 | +``` |
| 50 | + |
| 51 | +## Effect on API resilience |
| 52 | + |
| 53 | +Compacted has a trivial implementation and is marked as `@frozen` and `@inlinable`. This removes the ability of this type and functions to be ABI resilient boundaries at the benefit of being highly optimizable. |
| 54 | + |
| 55 | +## Alternatives considered |
| 56 | + |
| 57 | +None; shy of potentially eliding this since the functionality is so trivial. However the utility of this function aides in ease of use and approachability along with parity with the Swift Algorithms package. |
| 58 | + |
| 59 | +## Acknowledgments |
| 60 | + |
| 61 | +This transformation function is a direct analog to the synchronous version [defined in the Swift Algorithms package](https://github.com/apple/swift-algorithms/blob/main/Guides/Compacted.md) |
0 commit comments