Skip to content

Commit 6ff1f1b

Browse files
authored
Compacted proposal (#162)
1 parent 2a211e1 commit 6ff1f1b

File tree

1 file changed

+61
-0
lines changed

1 file changed

+61
-0
lines changed

Evolution/NNNN-compacted.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
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

Comments
 (0)