|
3 | 3 | # F# Async: FSharp.Control.AsyncSeq |
4 | 4 |
|
5 | 5 | An AsyncSeq is a sequence in which individual elements are retrieved using an `Async` computation. |
6 | | -It is similar to `seq<'a>` in that subsequent elements are pulled lazily. Structurally it is |
| 6 | +It is similar to `seq<'a>` in that subsequent elements are pulled on-demand. Structurally it is |
7 | 7 | similar to `list<'a>` with the difference being that each head and tail node or empty node is wrapped |
8 | | -in `Async`. `AsyncSeq` also bears similarity to `IObservable<'a>` with the former being pull-based and the |
9 | | -latter push-based. Analogs for most operations defined for `Seq`, `List` and `IObservable` are also defined for |
| 8 | +in `Async`. `AsyncSeq` also bears similarity to `IObservable<'a>` with the former being based on an "asynchronous pull" and the |
| 9 | +latter based on a "synchronous push". Analogs for most operations defined for `Seq`, `List` and `IObservable` are also defined for |
10 | 10 | `AsyncSeq`. The power of `AsyncSeq` lies in that many of these operations also have analogs based on `Async` |
11 | 11 | allowing composition of complex asynchronous workflows. |
12 | 12 |
|
@@ -144,9 +144,10 @@ in a blocking manner. |
144 | 144 | ### Comparison with IObservable<'a> |
145 | 145 |
|
146 | 146 | Both `IObservable<'a>` and `AsyncSeq<'a>` represent collections of items and both provide similar operations |
147 | | -for transformation and composition. The central difference between the two is that the former is push-based |
148 | | -and the latter is pull-based. Consumers of an `IObservable<'a>` *subscribe* to receive notifications about |
149 | | -new items or the end of the sequence. By contrast, consumers of an `AsyncSeq<'a>` *retrieve* subsequent items on their own |
| 147 | +for transformation and composition. The central difference between the two is that the former uses a *synchronous push* |
| 148 | +to a subscriber and the latter uses an *asynchronous pull* by a consumer. |
| 149 | +Consumers of an `IObservable<'a>` *subscribe* to receive notifications about |
| 150 | +new items or the end of the sequence. By contrast, consumers of an `AsyncSeq<'a>` *asynchronously retrieve* subsequent items on their own |
150 | 151 | terms. Some domains are more naturally modeled with one or the other, however it is less clear which is a more |
151 | 152 | suitable tool for a specific task. In many cases, a combination of the two provides the optimal solution and |
152 | 153 | restricting yourself to one, while simplifying the programming model, can lead one to view all problems as a nail. |
@@ -237,15 +238,15 @@ let storedTeetsObs' : IObservable<unit> = |
237 | 238 |
|
238 | 239 | (** |
239 | 240 | Overall, both solutions are succinct and composable and deciding which one to use can ultimately be a matter of preference. |
240 | | -Some things to consider are the push vs. pull semantics. On the one hand, tweets are pushed based - the consumer has no control |
| 241 | +Some things to consider are the "synchronous push" vs. "asynchronous pull" semantics. On the one hand, tweets are pushed based - the consumer has no control |
241 | 242 | over their generation. On the other hand, the program at hand will process the tweets on its own terms regardless of how quickly |
242 | 243 | they are being generated. Moreover, the underlying Twitter API will likely utilize a request-reply protocol to retrieve batches of |
243 | | -tweets from persistent storage. As such, the distinction between push vs. pull becomes less interesting. If the underlying source |
| 244 | +tweets from persistent storage. As such, the distinction between "synchronous push" vs. "asynchronous pull" becomes less interesting. If the underlying source |
244 | 245 | is truly push-based, then one can buffer its output and consume it using an asynchronous sequence. If the underlying source is pull-based, |
245 | 246 | then one can turn it into an observable sequence by first pulling, then pushing. Note however that in a true real-time reactive system, |
246 | 247 | notifications must be pushed immediately without delay. |
247 | 248 |
|
248 | | -Upon closer inspection, the consumption approaches between the two models aren't all too different. While `AsyncSeq` is pull based, |
| 249 | +Upon closer inspection, the consumption approaches between the two models aren't all too different. While `AsyncSeq` is based on an asynchronous-pull operation, |
249 | 250 | it is usually consumed using an operator such as `AsyncSeq.iterAsync` as shown above. This is a function of type |
250 | 251 | `('a -> Async<unit>) -> AsyncSeq<'a> -> Async<unit>` where the first argument is a function `'a -> Async<unit>` which performs |
251 | 252 | some work on an item of the sequence and is applied repeatedly to subsequent items. In a sense, `iterAsync` *pushes* values to this |
|
0 commit comments