You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
An implementation [`IAsyncEnumerable<'T>`][3] as a `taskSeq` CE for F# with accompanying `TaskSeq` module.
7
-
8
-
The `IAsyncEnumerable` interface was added to .NET in `.NET Core 3.0` and is part of `.NET Standard 2.1`. The main use-case was for iterative asynchronous enumeration over some resource. For instance, an event stream or a REST API interface with pagination, where each page is a [`MoveNextAsync`][4] call on the [`IAsyncEnumerator<'T>`][5] given by a call to [`GetAsyncEnumerator()`][6]. It has been relatively challenging to work properly with this type and dealing with each step being asynchronous, and the enumerator implementing [`IAsyncDisposable`][7] as well, which requires careful handling.
7
+
An implementation of [`IAsyncEnumerable<'T>`][3] as a computation expression: `taskSeq { ... }` with an accompanying `TaskSeq` module.
9
8
10
9
-----------------------------------------
11
10
@@ -18,10 +17,15 @@ The `IAsyncEnumerable` interface was added to .NET in `.NET Core 3.0` and is par
18
17
More info: https://marketplace.visualstudio.com/items?itemName=yzhang.markdown-all-in-one#table-of-contents
-[Futher reading on resumable state machines](#futher-reading-on-resumable-state-machines)
@@ -38,27 +42,134 @@ The `IAsyncEnumerable` interface was added to .NET in `.NET Core 3.0` and is par
38
42
39
43
-----------------------------------------
40
44
41
-
## Feature planning
45
+
## Overview
46
+
47
+
The `IAsyncEnumerable` interface was added to .NET in `.NET Core 3.0` and is part of `.NET Standard 2.1`. The main use-case was for iterative asynchronous enumeration over some resource. For instance, an event stream or a REST API interface with pagination, asynchronous reading over a list of files and accumulating the results, where each action can be modeled as a [`MoveNextAsync`][4] call on the [`IAsyncEnumerator<'T>`][5] given by a call to [`GetAsyncEnumerator()`][6].
48
+
49
+
Since the introduction of `task` in F# the call for a native implementation of _task sequences_ has grown, in particular because proper iterating over an `IAsyncEnumerable` has proven challenging, especially if one wants to avoid mutable variables. This library is an answer to that call and implements the same _resumable state machine_ approach with `taskSeq`.
50
+
51
+
### Module functions
52
+
53
+
As with `seq` and `Seq`, this library comes with a bunch of well-known collection functions, like `TaskSeq.empty`, `isEmpty` or `TaskSeq.map`, `iter`, `collect`, `fold` and `TaskSeq.find`, `pick`, `choose`, `filter`. Where applicable, these come with async variants, like `TaskSeq.mapAsync``iterAsync`, `collectAsync`, `foldAsync` and `TaskSeq.findAsync`, `pickAsync`, `chooseAsync`, `filterAsync`, which allows the applied function to be asynchronous.
54
+
55
+
[See below](#current-set-of-taskseq-utility-functions) for a full list of currently implemented functions and their variants.
56
+
57
+
### `taskSeq` computation expressions
58
+
59
+
The `taskSeq` computation expression can be used just like using `seq`. On top of that, it adds support for working with tasks through `let!` and
60
+
looping over a normal or asynchronous sequence (one that implements `IAsyncEnumerable<'T>'`). You can use `yield!` and `yield` and there's support
61
+
for `use` and `use!`, `try-with` and `try-finally` and `while` loops within the task sequence expression:
// returns a delayed sequence of IAsyncEnumerable<string>
120
+
let allFilesAsLines() = taskSeq {
121
+
let files = Directory.EnumerateFiles(@"c:\temp")
122
+
for file in files do
123
+
// await
124
+
let! contents = File.ReadAllLinesAsync file
125
+
// return all lines
126
+
yield! contents
127
+
}
128
+
129
+
let write file =
130
+
allFilesAsLines()
131
+
132
+
// synchronous map function on asynchronous task sequence
133
+
|> TaskSeq.map (fun x -> x.Replace("a", "b"))
134
+
135
+
// asynchronous map
136
+
|> TaskSeq.mapAsync (fun x -> task { return "hello: " + x })
137
+
138
+
// asynchronous iter
139
+
|> TaskSeq.iterAsync (fun data -> File.WriteAllTextAsync(fileName, data))
140
+
141
+
142
+
// infinite sequence
143
+
let feedFromTwitter user pwd = taskSeq {
144
+
do! loginToTwitterAsync(user, pwd)
145
+
while true do
146
+
let! message = getNextNextTwitterMessageAsync()
147
+
yield message
148
+
}
149
+
```
150
+
151
+
## Status & planning
152
+
153
+
This project has stable features currently, but before we go full "version one", we'd like to complete the surface area. This section covers the status of that, with a full list of implmented functions below. Here's the short list:
44
154
45
155
-[x] Stabilize and battle-test `taskSeq` resumable code. **DONE**
46
156
-[x] A growing set of module functions `TaskSeq`, see below for progress. **DONE & IN PROGRESS**
47
157
-[x] Packaging and publishing on Nuget, **DONE, PUBLISHED SINCE: 7 November 2022**. See https://www.nuget.org/packages/FSharp.Control.TaskSeq
48
158
-[x] Add `Async` variants for functions taking HOF arguments. **DONE**
49
159
-[ ] Add generated docs to <https://fsprojects.github.io>
50
-
-[ ] Expand surface area based on `AsyncSeq`.
51
-
-[ ] User requests?
160
+
-[ ] Expand surface area based on `AsyncSeq`. **ONGOING**
161
+
162
+
### Implementation progress
52
163
53
-
## Implementation progress
164
+
As of 9 November 2022: [Nuget package available][21]. In this phase, we will frequently update the package. Current:
The _resumable state machine_ backing the `taskSeq` CE is now finished and _restartability_ (not to be confused with _resumability_) has been implemented and stabilized. Full support for empty task sequences is done. Focus is now on adding functionality there, like adding more useful overloads for `yield` and `let!`. Suggestions are welcome!
170
+
The _resumable state machine_ backing the `taskSeq` CE is now finished and _restartability_ (not to be confused with _resumability_) has been implemented and stabilized. Full support for empty task sequences is done. Focus is now on adding functionality there, like adding more useful overloads for `yield` and `let!`. [Suggestions are welcome!][issues].
60
171
61
-
### `TaskSeq` module functions
172
+
### Progress and implemented `TaskSeq` module functions
62
173
63
174
We are working hard on getting a full set of module functions on `TaskSeq` that can be used with `IAsyncEnumerable` sequences. Our guide is the set of F# `Seq` functions in F# Core and, where applicable, the functions provided from `AsyncSeq`. Each implemented function is documented through XML doc comments to provide the necessary context-sensitive help.
@@ -26,19 +27,21 @@ An implementation of [`IAsyncEnumerable<'T>`][3] as a computation expression: `t
26
27
27
28
## Overview
28
29
29
-
The `IAsyncEnumerable` interface was added to .NET in `.NET Core 3.0` and is part of `.NET Standard 2.1`. The main use-case was for iterative asynchronous enumeration over some resource. For instance, an event stream or a REST API interface with pagination, asynchronous reading over a list of files and accumulating the results, where each action can be modeled as a [`MoveNextAsync`][4] call on the [`IAsyncEnumerator<'T>`][5] given by a call to [`GetAsyncEnumerator()`][6].
30
+
The `IAsyncEnumerable` interface was added to .NET in `.NET Core 3.0` and is part of `.NET Standard 2.1`. The main use-case was for iterative asynchronous enumeration over some resource. For instance, an event stream or a REST API interface with pagination, asynchronous reading over a list of files and accumulating the results, where each action can be modeled as a [`MoveNextAsync`][4] call on the [`IAsyncEnumerator<'T>`][5] given by a call to [`GetAsyncEnumerator()`][6].
30
31
31
32
Since the introduction of `task` in F# the call for a native implementation of _task sequences_ has grown, in particular because proper iterating over an `IAsyncEnumerable` has proven challenging, especially if one wants to avoid mutable variables. This library is an answer to that call and implements the same _resumable state machine_ approach with `taskSeq`.
32
33
33
-
As with `seq` and `Seq`, this library comes with a bunch of well-known collection functions, like `TaskSeq.empty/isEmpty` or `TaskSeq.map/iter/collect` and `TaskSeq.find/pick/choose/filter`. Where applicable, these come with async variants, like `TaskSeq.mapAsync/iterAsync/collectAsync` and `TaskSeq.findAsync/pickAsync/chooseAsync/filterAsync`, which allows the apply function to be asynchronous.
34
+
### Module functions
34
35
35
-
See below for a full list of currently implemented functions.
36
+
As with `seq` and `Seq`, this library comes with a bunch of well-known collection functions, like `TaskSeq.empty`, `isEmpty` or `TaskSeq.map`, `iter`, `collect`, `fold` and `TaskSeq.find`, `pick`, `choose`, `filter`. Where applicable, these come with async variants, like `TaskSeq.mapAsync``iterAsync`, `collectAsync`, `foldAsync` and `TaskSeq.findAsync`, `pickAsync`, `chooseAsync`, `filterAsync`, which allows the applied function to be asynchronous.
37
+
38
+
[See below](#taskseq-module-functions) for a full list of currently implemented functions and their variants.
36
39
37
40
### `taskSeq` computation expressions
38
41
39
-
The `taskSeq` computation expression can be used just like using `seq`. On top of that, it adds support for working with tasks through `let!` and
42
+
The `taskSeq` computation expression can be used just like using `seq`. On top of that, it adds support for working with tasks through `let!` and
40
43
looping over a normal or asynchronous sequence (one that implements `IAsyncEnumerable<'T>'`). You can use `yield!` and `yield` and there's support
41
-
for `use` and `use!`, `try-with` and `try-finally` and `while` loops within the task sequence expression:
44
+
for `use` and `use!`, `try-with` and `try-finally` and `while` loops within the task sequence expression.
0 commit comments