Skip to content

Commit cf70e78

Browse files
authored
Convert the chain guide to a proposal (apple#188)
1 parent be681c1 commit cf70e78

File tree

1 file changed

+80
-0
lines changed

1 file changed

+80
-0
lines changed

Diff for: Evolution/NNNN-chain.md

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# Chain
2+
3+
* Proposal: [SAA-NNNN](https://github.com/apple/swift-async-algorithms/blob/main/Evolution/NNNN-chain.md)
4+
* Authors: [Philippe Hausler](https://github.com/phausler)
5+
* Status: **Implemented**
6+
* Implementation: [[Source](https://github.com/apple/swift-async-algorithms/blob/main/Sources/AsyncAlgorithms/AsyncChain2Sequence.swift), [Source](https://github.com/apple/swift-async-algorithms/blob/main/Sources/AsyncAlgorithms/AsyncChain3Sequence.swift) |
7+
[Tests](https://github.com/apple/swift-async-algorithms/blob/main/Tests/AsyncAlgorithmsTests/TestChain.swift)]
8+
9+
## Introduction
10+
11+
Combining asynchronous sequences can occur in multiple ways. One such way that is common for non asynchronous sequences is iterating a prefix sequence and then iterating a suffix sequence. The asynchronous version is just as useful and common. This algorithm has been dubbed `chain` in the swift algorithms package.
12+
13+
The chain algorithm brings together two or more asynchronous sequences together sequentially where the elements from the resulting asynchronous sequence are comprised in order from the elements of the first asynchronous sequence and then the second (and so on) or until an error occurs.
14+
15+
This operation is available for all `AsyncSequence` types who share the same `Element` type.
16+
17+
```swift
18+
let preamble = [
19+
"// Some header to add as a preamble",
20+
"//",
21+
""
22+
].async
23+
let lines = chain(preamble, URL(fileURLWithPath: "/tmp/Sample.swift").lines)
24+
25+
for try await line in lines {
26+
print(line)
27+
}
28+
```
29+
30+
The above example shows how two `AsyncSequence` types can be chained together. In this case it prepends a preamble to the `lines` content of the file.
31+
32+
## Detailed Design
33+
34+
This function family and the associated family of return types are prime candidates for variadic generics. Until that proposal is accepted, these will be implemented in terms of two- and three-base sequence cases.
35+
36+
```swift
37+
public func chain<Base1: AsyncSequence, Base2: AsyncSequence>(_ s1: Base1, _ s2: Base2) -> AsyncChain2Sequence<Base1, Base2> where Base1.Element == Base2.Element
38+
39+
public func chain<Base1: AsyncSequence, Base2: AsyncSequence, Base3: AsyncSequence>(_ s1: Base1, _ s2: Base2, _ s3: Base3) -> AsyncChain3Sequence<Base1, Base2, Base3>
40+
41+
public struct AsyncChain2Sequence<Base1: AsyncSequence, Base2: AsyncSequence> where Base1.Element == Base2.Element {
42+
public typealias Element = Base1.Element
43+
44+
public struct Iterator: AsyncIteratorProtocol {
45+
public mutating func next() async rethrows -> Element?
46+
}
47+
48+
public func makeAsyncIterator() -> Iterator
49+
}
50+
51+
extension AsyncChain2Sequence: Sendable where Base1: Sendable, Base2: Sendable { }
52+
extension AsyncChain2Sequence.Iterator: Sendable where Base1.AsyncIterator: Sendable, Base2.AsyncIterator: Sendable { }
53+
54+
public struct AsyncChain3Sequence<Base1: AsyncSequence, Base2: AsyncSequence, Base3: AsyncSequence> where Base1.Element == Base2.Element, Base1.Element == Base3.Element {
55+
public typealias Element = Base1.Element
56+
57+
public struct Iterator: AsyncIteratorProtocol {
58+
public mutating func next() async rethrows -> Element?
59+
}
60+
61+
public func makeAsyncIterator() -> Iterator
62+
}
63+
64+
extension AsyncChain3Sequence: Sendable where Base1: Sendable, Base2: Sendable, Base3: Sendable { }
65+
extension AsyncChain3Sequence.Iterator: Sendable where Base1.AsyncIterator: Sendable, Base2.AsyncIterator: Sendable, Base3.AsyncIterator: Sendable { }
66+
```
67+
68+
The `chain(_:...)` function takes two or more sequences as arguments.
69+
70+
The resulting `AsyncChainSequence` type is an asynchronous sequence, with conditional conformance to `Sendable` when the arguments also conform to it.
71+
72+
When any of the asynchronous sequences being chained together come to their end of iteration, the `AsyncChainSequence` iteration proceeds to the next asynchronous sequence. When the last asynchronous sequence reaches the end of iteration, the `AsyncChainSequence` then ends its iteration.
73+
74+
At any point in time, if one of the comprising asynchronous sequences throws an error during iteration, the resulting `AsyncChainSequence` iteration will throw that error and end iteration. The throwing behavior of `AsyncChainSequence` is that it will throw when any of its comprising bases throw, and will not throw when all of its comprising bases do not throw.
75+
76+
### Naming
77+
78+
This function's and type's name match the term of art used in other languages and libraries.
79+
80+
This combinator function is a direct analog to the synchronous version [defined in the Swift Algorithms package](https://github.com/apple/swift-algorithms/blob/main/Guides/Chain.md).

0 commit comments

Comments
 (0)