Skip to content

Commit aae0634

Browse files
authored
Merge pull request #242 from fsprojects/release-v0.4.0
Prepare release v0.4.0
2 parents defb3c9 + 4737f76 commit aae0634

File tree

5 files changed

+101
-42
lines changed

5 files changed

+101
-42
lines changed

README.md

Lines changed: 65 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
# TaskSeq<!-- omit in toc -->
66

7-
An implementation of [`IAsyncEnumerable<'T>`][3] as a computation expression: `taskSeq { ... }` with an accompanying `TaskSeq` module, that allows seamless use of asynchronous sequences similar to F#'s native `seq` and `task` CE's.
7+
An implementation of [`IAsyncEnumerable<'T>`][3] as a computation expression: `taskSeq { ... }` with an accompanying `TaskSeq` module and functions, that allow seamless use of asynchronous sequences similar to F#'s native `seq` and `task` CE's.
88

99
* Latest stable version: [0.3.0 is on NuGet][nuget].
1010
* Latest prerelease version: [0.4.0-alpha.1 is on NuGet][nuget].
@@ -52,7 +52,7 @@ See [release notes.txt](release-notes.txt) for the version history of `TaskSeq`.
5252

5353
## Overview
5454

55-
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>`][3] given by a call to [`GetAsyncEnumerator()`][6].
55+
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, sequential 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>`][3] given by a call to [`GetAsyncEnumerator()`][6].
5656

5757
Since the introduction of `task` in F# the call for a native implementation of _task sequences_ has grown, in particular because proper iteration over an `IAsyncEnumerable` has proven challenging, especially if one wants to avoid mutable variables. This library is an answer to that call and applies the same _resumable state machine_ approach with `taskSeq`.
5858

@@ -92,7 +92,7 @@ F# Interactive (FSI):
9292
> #r "nuget: FSharp.Control.TaskSeq"
9393
9494
// or with specific version
95-
> #r "nuget: FSharp.Control.TaskSeq, 0.2.2"
95+
> #r "nuget: FSharp.Control.TaskSeq, 0.4.0"
9696
```
9797

9898
Paket:
@@ -111,7 +111,7 @@ As package reference in `fsproj` or `csproj` file:
111111

112112
```xml
113113
<!-- replace version with most recent version -->
114-
<PackageReference Include="FSharp.Control.TaskSeq" Version="0.2.2" />
114+
<PackageReference Include="FSharp.Control.TaskSeq" Version="0.4.0" />
115115
```
116116

117117
### Examples
@@ -196,24 +196,48 @@ There are more differences:
196196
| **[`Current`][5]** | [Returns `'T`][5] | n/a |
197197
| **Cancellation** | See [#133][], until 0.3.0: use `GetAsyncEnumerator(cancelToken)` | Implicit token flows to all subtasks per `async` semantics |
198198
| **Performance** | Very high, negligible allocations | Slower, more allocations, due to using `async` and cont style |
199-
| **Parallelism** | Possible with ChildTask; support will follow | Supported explicitly |
199+
| **Parallelism** | Unclear, interface is meant for _sequential/async_ processing | Supported by extension functions |
200200

201201
<sup>¹⁾ <a id="tsnote1"></a>_Both `AsyncSeq` and `TaskSeq` use a type called `IAsyncEnumerable<'T>`, but only `TaskSeq` uses the type from the BCL Generic Collections. `AsyncSeq` supports .NET Framework 4.6.x and NetStandard 2.0 as well, which do not have this type in the BCL._</sup>
202202

203203
## Status & planning
204204

205-
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 implemented functions below. Here's the shortlist:
206-
207-
- [x] Stabilize and battle-test `taskSeq` resumable code. **DONE**
208-
- [x] A growing set of module functions `TaskSeq`, see below for progress. **DONE & IN PROGRESS**
209-
- [x] Packaging and publishing on Nuget, **DONE, PUBLISHED SINCE: 7 November 2022**. See https://www.nuget.org/packages/FSharp.Control.TaskSeq
210-
- [x] Add `Async` variants for functions taking HOF arguments. **DONE**
211-
- [ ] Add generated docs to <https://fsprojects.github.io>
212-
- [ ] Expand surface area based on `AsyncSeq`. **ONGOING**
205+
The `TaskSeq` project already has a wide array of functions and functionalities, see overview below. The current status is: *STABLE*. However, certain features we'd really like to add:
206+
207+
- [x] Take existing `taskSeq` resumable code from F# and fix it. **DONE**
208+
- [x] Add almost all functions from `Seq` that could apply to `TaskSeq` (full overview below). **MOSTLY DONE, STILL TODO**
209+
- [ ] Add remaining relevant functions from `Seq`. **PLANNED FOR 0.4.x**
210+
- [x] `min` / `max` / `minBy` / `maxBy` & async variant (see [#221])
211+
- [x] `insertAt` / `updateAt` and related (see [#236])
212+
- [ ] `average` / `averageBy`, `sum` and related
213+
- [x] `forall` / `forallAsync` (see [#240])
214+
- [x] `skip` / `drop` / `truncate` / `take` (see [#209])
215+
- [ ] `chunkBySize` / `windowed`
216+
- [ ] `compareWith`
217+
- [ ] `distinct`
218+
- [ ] `exists2` / `map2` / `fold2` / `iter2` and related '2'-functions
219+
- [ ] `mapFold`
220+
- [ ] `pairwise` / `allpairs` / `permute` / `distinct` / `distinctBy`
221+
- [ ] `replicate`
222+
- [ ] `reduce` / `scan`
223+
- [ ] `unfold`
224+
- [x] Publish package on Nuget, **DONE, PUBLISHED SINCE: 7 November 2022**. See https://www.nuget.org/packages/FSharp.Control.TaskSeq
225+
- [x] Make `TaskSeq` interoperable with `Task` by expanding the latter with a `for .. in .. do` that acceps task sequences
226+
- [x] Add to/from functions to seq, list, array
227+
- [ ] Add applicable functions from `AsyncSeq`. **PLANNED FOR 0.5-alpha**
228+
- [ ] (Better) support for cancellations
229+
- [ ] Make the tasks cancellable with token (see [#133]). **PLANNED FOR 0.5-alpha**
230+
- [ ] Support `ConfiguredCancelableAsyncEnumerable` (see [#167]). **PLANNED FOR 0.5-alpha**
231+
- [ ] Interop with `cancellableTask` and `valueTask` from [`IcedTasks`][24]
232+
- [ ] Interop with `AsyncSeq`.
233+
- [ ] (maybe) Support any awaitable type in the function lib (that is: where a `Task` is required, accept a `ValueTask` and `Async` as well)
234+
- [ ] Add `TaskEx` functionality (separate lib). **DISCUSSION**
235+
- [ ] Move documentation to <https://fsprojects.github.io>
213236

214237
### Implementation progress
215238

216-
As of 9 November 2022: [Nuget package available][21]. In this phase, we will frequently update the package, see [release notes.txt](release-notes.txt). Current version:
239+
* As of 9 November 2022: [Nuget package available][21]. In this phase, we will frequently update the package, see [release notes.txt](release-notes.txt). Current version:
240+
* Major update: 17 March 2024, version 0.4.0
217241

218242
[![Nuget](https://img.shields.io/nuget/vpre/FSharp.Control.TaskSeq)](https://www.nuget.org/packages/FSharp.Control.TaskSeq/)
219243

@@ -245,10 +269,15 @@ This is what has been implemented so far, is planned or skipped:
245269
| &#x2705; [#11][] | | `collectSeq` | `collectSeqAsync` | |
246270
| | `compareWith` | `compareWith` | `compareWithAsync` | |
247271
| &#x2705; [#69][] | `concat` | `concat` | | |
272+
| &#x2705; [#237][]| `concat` (list) | `concat` (list) | | |
273+
| &#x2705; [#237][]| `concat` (array) | `concat` (array) | | |
274+
| &#x2705; [#237][]| `concat` (r-array) | `concat` (r-array) | | |
275+
| &#x2705; [#237][]| `concat` (seq) | `concat` (seq) | | |
248276
| &#x2705; [#70][] | `contains` | `contains` | | |
249277
| &#x2705; [#82][] | `delay` | `delay` | | |
250278
| | `distinct` | `distinct` | | |
251279
| | `distinctBy` | `dictinctBy` | `distinctByAsync` | |
280+
| &#x2705; [#209][]| | `drop` | | |
252281
| &#x2705; [#2][] | `empty` | `empty` | | |
253282
| &#x2705; [#23][] | `exactlyOne` | `exactlyOne` | | |
254283
| &#x2705; [#83][] | `except` | `except` | | |
@@ -264,15 +293,15 @@ This is what has been implemented so far, is planned or skipped:
264293
| | `fold2` | `fold2` | `fold2Async` | |
265294
| &#x1f6ab; | `foldBack` | | | [note #2](#note2 "Because of the async nature of TaskSeq sequences, iterating from the back would be bad practice. Instead, materialize the sequence to a list or array and then apply the 'Back' iterators.") |
266295
| &#x1f6ab; | `foldBack2` | | | [note #2](#note2 "Because of the async nature of TaskSeq sequences, iterating from the back would be bad practice. Instead, materialize the sequence to a list or array and then apply the 'Back' iterators.") |
267-
| | `forall` | `forall` | `forallAsync` | |
296+
| &#x2705; [#240][]| `forall` | `forall` | `forallAsync` | |
268297
| | `forall2` | `forall2` | `forall2Async` | |
269298
| &#x2753; | `groupBy` | `groupBy` | `groupByAsync` | [note #1](#note1 "These functions require a form of pre-materializing through 'TaskSeq.cache', similar to the approach taken in the corresponding 'Seq' functions. It doesn't make much sense to have a cached async sequence. However, 'AsyncSeq' does implement these, so we'll probably do so eventually as well.") |
270299
| &#x2705; [#23][] | `head` | `head` | | |
271300
| &#x2705; [#68][] | `indexed` | `indexed` | | |
272301
| &#x2705; [#69][] | `init` | `init` | `initAsync` | |
273302
| &#x2705; [#69][] | `initInfinite` | `initInfinite` | `initInfiniteAsync` | |
274-
| | `insertAt` | `insertAt` | | |
275-
| | `insertManyAt` | `insertManyAt` | | |
303+
| &#x2705; [#236][]| `insertAt` | `insertAt` | | |
304+
| &#x2705; [#236][]| `insertManyAt` | `insertManyAt` | | |
276305
| &#x2705; [#23][] | `isEmpty` | `isEmpty` | | |
277306
| &#x2705; [#23][] | `item` | `item` | | |
278307
| &#x2705; [#2][] | `iter` | `iter` | `iterAsync` | |
@@ -310,15 +339,14 @@ This is what has been implemented so far, is planned or skipped:
310339
| &#x1f6ab; | `readOnly` | | | [note #3](#note3 "The motivation for 'readOnly' in 'Seq' is that a cast from a mutable array or list to a 'seq<_>' is valid and can be cast back, leading to a mutable sequence. Since 'TaskSeq' doesn't implement 'IEnumerable<_>', such casts are not possible.") |
311340
| | `reduce` | `reduce` | `reduceAsync` | |
312341
| &#x1f6ab; | `reduceBack` | | | [note #2](#note2 "Because of the async nature of TaskSeq sequences, iterating from the back would be bad practice. Instead, materialize the sequence to a list or array and then apply the 'Back' iterators.") |
313-
| | `removeAt` | `removeAt` | | |
314-
| | `removeManyAt` | `removeManyAt` | | |
342+
| &#x2705; [#236][]| `removeAt` | `removeAt` | | |
343+
| &#x2705; [#236][]| `removeManyAt` | `removeManyAt` | | |
315344
| | `replicate` | `replicate` | | |
316345
| &#x2753; | `rev` | | | [note #1](#note1 "These functions require a form of pre-materializing through 'TaskSeq.cache', similar to the approach taken in the corresponding 'Seq' functions. It doesn't make much sense to have a cached async sequence. However, 'AsyncSeq' does implement these, so we'll probably do so eventually as well.") |
317346
| | `scan` | `scan` | `scanAsync` | |
318347
| &#x1f6ab; | `scanBack` | | | [note #2](#note2 "Because of the async nature of TaskSeq sequences, iterating from the back would be bad practice. Instead, materialize the sequence to a list or array and then apply the 'Back' iterators.") |
319348
| &#x2705; [#90][] | `singleton` | `singleton` | | |
320349
| &#x2705; [#209][]| `skip` | `skip` | | |
321-
| &#x2705; [#209][]| | `drop` | | |
322350
| &#x2705; [#219][]| `skipWhile` | `skipWhile` | `skipWhileAsync` | |
323351
| &#x2705; [#219][]| | `skipWhileInclusive` | `skipWhileInclusiveAsync` | |
324352
| &#x2753; | `sort` | | | [note #1](#note1 "These functions require a form of pre-materializing through 'TaskSeq.cache', similar to the approach taken in the corresponding 'Seq' functions. It doesn't make much sense to have a cached async sequence. However, 'AsyncSeq' does implement these, so we'll probably do so eventually as well.") |
@@ -352,7 +380,7 @@ This is what has been implemented so far, is planned or skipped:
352380
| &#x2705; [#23][] | `tryPick` | `tryPick` | `tryPickAsync` | |
353381
| &#x2705; [#76][] | | `tryTail` | | |
354382
| | `unfold` | `unfold` | `unfoldAsync` | |
355-
| | `updateAt` | `updateAt` | | |
383+
| &#x2705; [#236][]| `updateAt` | `updateAt` | | |
356384
| &#x2705; [#217][]| `where` | `where` | `whereAsync` | |
357385
| | `windowed` | `windowed` | | |
358386
| &#x2705; [#2][] | `zip` | `zip` | | |
@@ -473,6 +501,10 @@ module TaskSeq =
473501
val collectSeq: binder: ('T -> #seq<'U>) -> source: TaskSeq<'T> -> TaskSeq<'U>
474502
val collectSeqAsync: binder: ('T -> #Task<'SeqU>) -> source: TaskSeq<'T> -> TaskSeq<'U> when 'SeqU :> seq<'U>
475503
val concat: sources: TaskSeq<#TaskSeq<'T>> -> TaskSeq<'T>
504+
val concat: sources: TaskSeq<'T seq> -> TaskSeq<'T>
505+
val concat: sources: TaskSeq<'T list> -> TaskSeq<'T>
506+
val concat: sources: TaskSeq<'T array> -> TaskSeq<'T>
507+
val concat: sources: TaskSeq<ResizeArray<'T>> -> TaskSeq<'T>
476508
val contains<'T when 'T: equality> : value: 'T -> source: TaskSeq<'T> -> Task<bool>
477509
val delay: generator: (unit -> TaskSeq<'T>) -> TaskSeq<'T>
478510
val drop: count: int -> source: TaskSeq<'T> -> TaskSeq<'T>
@@ -490,12 +522,16 @@ module TaskSeq =
490522
val findIndexAsync: predicate: ('T -> #Task<bool>) -> source: TaskSeq<'T> -> Task<int>
491523
val fold: folder: ('State -> 'T -> 'State) -> state: 'State -> source: TaskSeq<'T> -> Task<'State>
492524
val foldAsync: folder: ('State -> 'T -> #Task<'State>) -> state: 'State -> source: TaskSeq<'T> -> Task<'State>
525+
val forall: predicate: ('T -> bool) -> source: TaskSeq<'T> -> Task<bool>
526+
val forallAsync: predicate: ('T -> #Task<bool>) -> source: TaskSeq<'T> -> Task<bool>
493527
val head: source: TaskSeq<'T> -> Task<'T>
494528
val indexed: source: TaskSeq<'T> -> TaskSeq<int * 'T>
495529
val init: count: int -> initializer: (int -> 'T) -> TaskSeq<'T>
496530
val initAsync: count: int -> initializer: (int -> #Task<'T>) -> TaskSeq<'T>
497531
val initInfinite: initializer: (int -> 'T) -> TaskSeq<'T>
498532
val initInfiniteAsync: initializer: (int -> #Task<'T>) -> TaskSeq<'T>
533+
val insertAt: position:int -> value:'T -> source: TaskSeq<'T> -> TaskSeq<'T>
534+
val insertManyAt: position:int -> values:TaskSeq<'T> -> source: TaskSeq<'T> -> TaskSeq<'T>
499535
val isEmpty: source: TaskSeq<'T> -> Task<bool>
500536
val item: index: int -> source: TaskSeq<'T> -> Task<'T>
501537
val iter: action: ('T -> unit) -> source: TaskSeq<'T> -> Task<unit>
@@ -529,6 +565,8 @@ module TaskSeq =
529565
val pick: chooser: ('T -> 'U option) -> source: TaskSeq<'T> -> Task<'U>
530566
val pickAsync: chooser: ('T -> #Task<'U option>) -> source: TaskSeq<'T> -> Task<'U>
531567
val prependSeq: source1: seq<'T> -> source2: TaskSeq<'T> -> TaskSeq<'T>
568+
val removeAt: position:int -> source: TaskSeq<'T> -> TaskSeq<'T>
569+
val removeManyAt: position:int -> count:int -> source: TaskSeq<'T> -> TaskSeq<'T>
532570
val singleton: source: 'T -> TaskSeq<'T>
533571
val skip: count: int -> source: TaskSeq<'T> -> TaskSeq<'T>
534572
val tail: source: TaskSeq<'T> -> Task<TaskSeq<'T>>
@@ -559,6 +597,7 @@ module TaskSeq =
559597
val where: predicate: ('T -> bool) -> source: TaskSeq<'T> -> TaskSeq<'T>
560598
val whereAsync: predicate: ('T -> #Task<bool>) -> source: TaskSeq<'T> -> TaskSeq<'T>
561599
val unbox<'U when 'U: struct> : source: TaskSeq<obj> -> TaskSeq<'U>
600+
val updateAt: position:int -> value:'T -> source: TaskSeq<'T> -> TaskSeq<'T>
562601
val zip: source1: TaskSeq<'T> -> source2: TaskSeq<'U> -> TaskSeq<'T * 'U>
563602
```
564603

@@ -590,6 +629,7 @@ module TaskSeq =
590629
[21]: https://www.nuget.org/packages/FSharp.Control.TaskSeq#versions-body-tab
591630
[22]: https://fsprojects.github.io/FSharp.Control.AsyncSeq/reference/fsharp-control-asyncseq.html#toAsyncEnum
592631
[23]: https://fsprojects.github.io/FSharp.Control.AsyncSeq/reference/fsharp-control-asyncseq.html#fromAsyncEnum
632+
[24]: https://github.com/TheAngryByrd/IcedTasks
593633

594634
[#2]: https://github.com/fsprojects/FSharp.Control.TaskSeq/pull/2
595635
[#11]: https://github.com/fsprojects/FSharp.Control.TaskSeq/pull/11
@@ -606,10 +646,14 @@ module TaskSeq =
606646
[#90]: https://github.com/fsprojects/FSharp.Control.TaskSeq/pull/90
607647
[#126]: https://github.com/fsprojects/FSharp.Control.TaskSeq/pull/126
608648
[#133]: https://github.com/fsprojects/FSharp.Control.TaskSeq/issues/133
649+
[#167]: https://github.com/fsprojects/FSharp.Control.TaskSeq/issues/167
609650
[#209]: https://github.com/fsprojects/FSharp.Control.TaskSeq/issues/209
610651
[#217]: https://github.com/fsprojects/FSharp.Control.TaskSeq/issues/217
611652
[#219]: https://github.com/fsprojects/FSharp.Control.TaskSeq/issues/219
612653
[#221]: https://github.com/fsprojects/FSharp.Control.TaskSeq/issues/221
654+
[#237]: https://github.com/fsprojects/FSharp.Control.TaskSeq/issues/237
655+
[#236]: https://github.com/fsprojects/FSharp.Control.TaskSeq/issues/236
656+
[#240]: https://github.com/fsprojects/FSharp.Control.TaskSeq/issues/240
613657

614658
[issues]: https://github.com/fsprojects/FSharp.Control.TaskSeq/issues
615659
[nuget]: https://www.nuget.org/packages/FSharp.Control.TaskSeq/

Version.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<Project>
22
<PropertyGroup>
33
<!-- updating this version will trigger a publish after merge to 'main' -->
4-
<Version>0.4.0-alpha.1</Version>
4+
<Version>0.4.0</Version>
55
</PropertyGroup>
66
</Project>

0 commit comments

Comments
 (0)