Skip to content

Commit ebfd880

Browse files
committed
Proposal for joined and joined(separator:)
1 parent 57072e6 commit ebfd880

File tree

1 file changed

+66
-0
lines changed

1 file changed

+66
-0
lines changed

Evolution/NNNN-joined.md

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# Joined
2+
3+
* Proposal: [SAA-NNNN](https://github.com/apple/swift-async-algorithms/blob/main/Evolution/NNNN-joined.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/AsyncJoinedSequence.swift) |
8+
[Tests](https://github.com/apple/swift-async-algorithms/blob/main/Tests/AsyncAlgorithmsTests/TestJoin.swift)]
9+
* Decision Notes:
10+
* Bugs:
11+
12+
## Introduction
13+
14+
The `joined()` and `joined(separator:)` algorithms on `AsyncSequence`s provide APIs to concatenate an `AsyncSequence` of `AsyncSequence`s.
15+
16+
```swift
17+
extension AsyncSequence where Element: AsyncSequence {
18+
public func joined() -> AsyncJoinedSequence<Self>
19+
}
20+
21+
extension AsyncSequence where Element: AsyncSequence {
22+
public func joined<Separator: AsyncSequence>(separator: Separator) -> AsyncJoinedBySeparatorSequence<Self, Separator>
23+
}
24+
```
25+
26+
## Detailed Design
27+
28+
These algorithms iterate over the elements of each `AsyncSequence` one bye one, i.e. only after the iteration of one `AsyncSequence` has finished the next one will be started.
29+
30+
```swift
31+
let appleFeed = URL("http://www.example.com/ticker?symbol=AAPL").lines
32+
let nasdaqFeed = URL("http://www.example.com/ticker?symbol=^IXIC").lines
33+
34+
for try await line in [appleFeed, nasdaqFeed].joined() {
35+
print("\(line)")
36+
}
37+
```
38+
39+
Given some sample inputs the following combined events can be expected.
40+
41+
| Timestamp | appleFeed | nasdaqFeed | output |
42+
| ----------- | --------- | ---------- | ----------------------------- |
43+
| 11:40 AM | 173.91 | | 173.91 |
44+
| 12:25 AM | | 14236.78 | |
45+
| 12:40 AM | | 14218.34 | |
46+
| 1:15 PM | 173.00 | | 173.00 |
47+
| 1:15 PM | | | 14236.78 |
48+
| 1:15 PM | | | 14218.34 |
49+
50+
51+
The `joined()` and `joined(separator:)` methods are available on `AsyncSequence`s with elements that are `AsyncSequence`s themselves and produce either an `AsyncJoinedSequence` or an `AsyncJoinedBySeparatorSequence`.
52+
53+
As soon as an inner `AsyncSequence` returns `nil` the algorithm continues with iterating the next inner `AsyncSequence`.
54+
55+
The throwing behaviour of `AsyncJoinedSequence` and `AsyncJoinedBySeparatorSequence` is that if any of the inner `AsyncSequence`s throws, then the composed sequence throws on its iteration.
56+
57+
### Naming
58+
59+
The naming follows to current method naming of the standard library's [`joined`](https://developer.apple.com/documentation/swift/array/joined(separator:)-7uber) method.
60+
Prior art in the reactive community often names this method `concat`; however, we think that an alignment with the current method on `Sequence` are better.
61+
62+
### Comparison with other libraries
63+
64+
**ReactiveX** ReactiveX has an [API definition of Concat](https://reactivex.io/documentation/operators/concat.html) as a top level function for concatenating Observables.
65+
66+
**Combine** Combine has an [API definition of append](https://developer.apple.com/documentation/combine/publisher/append(_:)-5yh02) which offers similar functionality but limited to concatenating two individual `Publisher`s.

0 commit comments

Comments
 (0)