diff --git a/README.md b/README.md index 13b7c26c..9cf9bcdd 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ # TaskSeq -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. +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. * Latest stable version: [0.3.0 is on NuGet][nuget]. * 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`. ## Overview -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]. +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]. 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`. @@ -92,7 +92,7 @@ F# Interactive (FSI): > #r "nuget: FSharp.Control.TaskSeq" // or with specific version -> #r "nuget: FSharp.Control.TaskSeq, 0.2.2" +> #r "nuget: FSharp.Control.TaskSeq, 0.4.0" ``` Paket: @@ -111,7 +111,7 @@ As package reference in `fsproj` or `csproj` file: ```xml - + ``` ### Examples @@ -196,24 +196,48 @@ There are more differences: | **[`Current`][5]** | [Returns `'T`][5] | n/a | | **Cancellation** | See [#133][], until 0.3.0: use `GetAsyncEnumerator(cancelToken)` | Implicit token flows to all subtasks per `async` semantics | | **Performance** | Very high, negligible allocations | Slower, more allocations, due to using `async` and cont style | -| **Parallelism** | Possible with ChildTask; support will follow | Supported explicitly | +| **Parallelism** | Unclear, interface is meant for _sequential/async_ processing | Supported by extension functions | ¹⁾ _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._ ## Status & planning -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: - -- [x] Stabilize and battle-test `taskSeq` resumable code. **DONE** -- [x] A growing set of module functions `TaskSeq`, see below for progress. **DONE & IN PROGRESS** -- [x] Packaging and publishing on Nuget, **DONE, PUBLISHED SINCE: 7 November 2022**. See https://www.nuget.org/packages/FSharp.Control.TaskSeq -- [x] Add `Async` variants for functions taking HOF arguments. **DONE** -- [ ] Add generated docs to -- [ ] Expand surface area based on `AsyncSeq`. **ONGOING** +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: + +- [x] Take existing `taskSeq` resumable code from F# and fix it. **DONE** +- [x] Add almost all functions from `Seq` that could apply to `TaskSeq` (full overview below). **MOSTLY DONE, STILL TODO** +- [ ] Add remaining relevant functions from `Seq`. **PLANNED FOR 0.4.x** + - [x] `min` / `max` / `minBy` / `maxBy` & async variant (see [#221]) + - [x] `insertAt` / `updateAt` and related (see [#236]) + - [ ] `average` / `averageBy`, `sum` and related + - [x] `forall` / `forallAsync` (see [#240]) + - [x] `skip` / `drop` / `truncate` / `take` (see [#209]) + - [ ] `chunkBySize` / `windowed` + - [ ] `compareWith` + - [ ] `distinct` + - [ ] `exists2` / `map2` / `fold2` / `iter2` and related '2'-functions + - [ ] `mapFold` + - [ ] `pairwise` / `allpairs` / `permute` / `distinct` / `distinctBy` + - [ ] `replicate` + - [ ] `reduce` / `scan` + - [ ] `unfold` +- [x] Publish package on Nuget, **DONE, PUBLISHED SINCE: 7 November 2022**. See https://www.nuget.org/packages/FSharp.Control.TaskSeq +- [x] Make `TaskSeq` interoperable with `Task` by expanding the latter with a `for .. in .. do` that acceps task sequences +- [x] Add to/from functions to seq, list, array +- [ ] Add applicable functions from `AsyncSeq`. **PLANNED FOR 0.5-alpha** +- [ ] (Better) support for cancellations + - [ ] Make the tasks cancellable with token (see [#133]). **PLANNED FOR 0.5-alpha** + - [ ] Support `ConfiguredCancelableAsyncEnumerable` (see [#167]). **PLANNED FOR 0.5-alpha** + - [ ] Interop with `cancellableTask` and `valueTask` from [`IcedTasks`][24] +- [ ] Interop with `AsyncSeq`. +- [ ] (maybe) Support any awaitable type in the function lib (that is: where a `Task` is required, accept a `ValueTask` and `Async` as well) +- [ ] Add `TaskEx` functionality (separate lib). **DISCUSSION** +- [ ] Move documentation to ### Implementation progress -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: + * 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: + * Major update: 17 March 2024, version 0.4.0 [![Nuget](https://img.shields.io/nuget/vpre/FSharp.Control.TaskSeq)](https://www.nuget.org/packages/FSharp.Control.TaskSeq/) @@ -245,10 +269,15 @@ This is what has been implemented so far, is planned or skipped: | ✅ [#11][] | | `collectSeq` | `collectSeqAsync` | | | | `compareWith` | `compareWith` | `compareWithAsync` | | | ✅ [#69][] | `concat` | `concat` | | | +| ✅ [#237][]| `concat` (list) | `concat` (list) | | | +| ✅ [#237][]| `concat` (array) | `concat` (array) | | | +| ✅ [#237][]| `concat` (r-array) | `concat` (r-array) | | | +| ✅ [#237][]| `concat` (seq) | `concat` (seq) | | | | ✅ [#70][] | `contains` | `contains` | | | | ✅ [#82][] | `delay` | `delay` | | | | | `distinct` | `distinct` | | | | | `distinctBy` | `dictinctBy` | `distinctByAsync` | | +| ✅ [#209][]| | `drop` | | | | ✅ [#2][] | `empty` | `empty` | | | | ✅ [#23][] | `exactlyOne` | `exactlyOne` | | | | ✅ [#83][] | `except` | `except` | | | @@ -264,15 +293,15 @@ This is what has been implemented so far, is planned or skipped: | | `fold2` | `fold2` | `fold2Async` | | | 🚫 | `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.") | | 🚫 | `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.") | -| | `forall` | `forall` | `forallAsync` | | +| ✅ [#240][]| `forall` | `forall` | `forallAsync` | | | | `forall2` | `forall2` | `forall2Async` | | | ❓ | `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.") | | ✅ [#23][] | `head` | `head` | | | | ✅ [#68][] | `indexed` | `indexed` | | | | ✅ [#69][] | `init` | `init` | `initAsync` | | | ✅ [#69][] | `initInfinite` | `initInfinite` | `initInfiniteAsync` | | -| | `insertAt` | `insertAt` | | | -| | `insertManyAt` | `insertManyAt` | | | +| ✅ [#236][]| `insertAt` | `insertAt` | | | +| ✅ [#236][]| `insertManyAt` | `insertManyAt` | | | | ✅ [#23][] | `isEmpty` | `isEmpty` | | | | ✅ [#23][] | `item` | `item` | | | | ✅ [#2][] | `iter` | `iter` | `iterAsync` | | @@ -310,15 +339,14 @@ This is what has been implemented so far, is planned or skipped: | 🚫 | `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.") | | | `reduce` | `reduce` | `reduceAsync` | | | 🚫 | `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.") | -| | `removeAt` | `removeAt` | | | -| | `removeManyAt` | `removeManyAt` | | | +| ✅ [#236][]| `removeAt` | `removeAt` | | | +| ✅ [#236][]| `removeManyAt` | `removeManyAt` | | | | | `replicate` | `replicate` | | | | ❓ | `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.") | | | `scan` | `scan` | `scanAsync` | | | 🚫 | `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.") | | ✅ [#90][] | `singleton` | `singleton` | | | | ✅ [#209][]| `skip` | `skip` | | | -| ✅ [#209][]| | `drop` | | | | ✅ [#219][]| `skipWhile` | `skipWhile` | `skipWhileAsync` | | | ✅ [#219][]| | `skipWhileInclusive` | `skipWhileInclusiveAsync` | | | ❓ | `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: | ✅ [#23][] | `tryPick` | `tryPick` | `tryPickAsync` | | | ✅ [#76][] | | `tryTail` | | | | | `unfold` | `unfold` | `unfoldAsync` | | -| | `updateAt` | `updateAt` | | | +| ✅ [#236][]| `updateAt` | `updateAt` | | | | ✅ [#217][]| `where` | `where` | `whereAsync` | | | | `windowed` | `windowed` | | | | ✅ [#2][] | `zip` | `zip` | | | @@ -473,6 +501,10 @@ module TaskSeq = val collectSeq: binder: ('T -> #seq<'U>) -> source: TaskSeq<'T> -> TaskSeq<'U> val collectSeqAsync: binder: ('T -> #Task<'SeqU>) -> source: TaskSeq<'T> -> TaskSeq<'U> when 'SeqU :> seq<'U> val concat: sources: TaskSeq<#TaskSeq<'T>> -> TaskSeq<'T> + val concat: sources: TaskSeq<'T seq> -> TaskSeq<'T> + val concat: sources: TaskSeq<'T list> -> TaskSeq<'T> + val concat: sources: TaskSeq<'T array> -> TaskSeq<'T> + val concat: sources: TaskSeq> -> TaskSeq<'T> val contains<'T when 'T: equality> : value: 'T -> source: TaskSeq<'T> -> Task val delay: generator: (unit -> TaskSeq<'T>) -> TaskSeq<'T> val drop: count: int -> source: TaskSeq<'T> -> TaskSeq<'T> @@ -490,12 +522,16 @@ module TaskSeq = val findIndexAsync: predicate: ('T -> #Task) -> source: TaskSeq<'T> -> Task val fold: folder: ('State -> 'T -> 'State) -> state: 'State -> source: TaskSeq<'T> -> Task<'State> val foldAsync: folder: ('State -> 'T -> #Task<'State>) -> state: 'State -> source: TaskSeq<'T> -> Task<'State> + val forall: predicate: ('T -> bool) -> source: TaskSeq<'T> -> Task + val forallAsync: predicate: ('T -> #Task) -> source: TaskSeq<'T> -> Task val head: source: TaskSeq<'T> -> Task<'T> val indexed: source: TaskSeq<'T> -> TaskSeq val init: count: int -> initializer: (int -> 'T) -> TaskSeq<'T> val initAsync: count: int -> initializer: (int -> #Task<'T>) -> TaskSeq<'T> val initInfinite: initializer: (int -> 'T) -> TaskSeq<'T> val initInfiniteAsync: initializer: (int -> #Task<'T>) -> TaskSeq<'T> + val insertAt: position:int -> value:'T -> source: TaskSeq<'T> -> TaskSeq<'T> + val insertManyAt: position:int -> values:TaskSeq<'T> -> source: TaskSeq<'T> -> TaskSeq<'T> val isEmpty: source: TaskSeq<'T> -> Task val item: index: int -> source: TaskSeq<'T> -> Task<'T> val iter: action: ('T -> unit) -> source: TaskSeq<'T> -> Task @@ -529,6 +565,8 @@ module TaskSeq = val pick: chooser: ('T -> 'U option) -> source: TaskSeq<'T> -> Task<'U> val pickAsync: chooser: ('T -> #Task<'U option>) -> source: TaskSeq<'T> -> Task<'U> val prependSeq: source1: seq<'T> -> source2: TaskSeq<'T> -> TaskSeq<'T> + val removeAt: position:int -> source: TaskSeq<'T> -> TaskSeq<'T> + val removeManyAt: position:int -> count:int -> source: TaskSeq<'T> -> TaskSeq<'T> val singleton: source: 'T -> TaskSeq<'T> val skip: count: int -> source: TaskSeq<'T> -> TaskSeq<'T> val tail: source: TaskSeq<'T> -> Task> @@ -559,6 +597,7 @@ module TaskSeq = val where: predicate: ('T -> bool) -> source: TaskSeq<'T> -> TaskSeq<'T> val whereAsync: predicate: ('T -> #Task) -> source: TaskSeq<'T> -> TaskSeq<'T> val unbox<'U when 'U: struct> : source: TaskSeq -> TaskSeq<'U> + val updateAt: position:int -> value:'T -> source: TaskSeq<'T> -> TaskSeq<'T> val zip: source1: TaskSeq<'T> -> source2: TaskSeq<'U> -> TaskSeq<'T * 'U> ``` @@ -590,6 +629,7 @@ module TaskSeq = [21]: https://www.nuget.org/packages/FSharp.Control.TaskSeq#versions-body-tab [22]: https://fsprojects.github.io/FSharp.Control.AsyncSeq/reference/fsharp-control-asyncseq.html#toAsyncEnum [23]: https://fsprojects.github.io/FSharp.Control.AsyncSeq/reference/fsharp-control-asyncseq.html#fromAsyncEnum +[24]: https://github.com/TheAngryByrd/IcedTasks [#2]: https://github.com/fsprojects/FSharp.Control.TaskSeq/pull/2 [#11]: https://github.com/fsprojects/FSharp.Control.TaskSeq/pull/11 @@ -606,10 +646,14 @@ module TaskSeq = [#90]: https://github.com/fsprojects/FSharp.Control.TaskSeq/pull/90 [#126]: https://github.com/fsprojects/FSharp.Control.TaskSeq/pull/126 [#133]: https://github.com/fsprojects/FSharp.Control.TaskSeq/issues/133 +[#167]: https://github.com/fsprojects/FSharp.Control.TaskSeq/issues/167 [#209]: https://github.com/fsprojects/FSharp.Control.TaskSeq/issues/209 [#217]: https://github.com/fsprojects/FSharp.Control.TaskSeq/issues/217 [#219]: https://github.com/fsprojects/FSharp.Control.TaskSeq/issues/219 [#221]: https://github.com/fsprojects/FSharp.Control.TaskSeq/issues/221 +[#237]: https://github.com/fsprojects/FSharp.Control.TaskSeq/issues/237 +[#236]: https://github.com/fsprojects/FSharp.Control.TaskSeq/issues/236 +[#240]: https://github.com/fsprojects/FSharp.Control.TaskSeq/issues/240 [issues]: https://github.com/fsprojects/FSharp.Control.TaskSeq/issues [nuget]: https://www.nuget.org/packages/FSharp.Control.TaskSeq/ diff --git a/Version.props b/Version.props index 1b14afd5..2ec8b638 100644 --- a/Version.props +++ b/Version.props @@ -1,6 +1,6 @@ - 0.4.0-alpha.1 + 0.4.0 \ No newline at end of file diff --git a/assets/nuget-package-readme.md b/assets/nuget-package-readme.md index ceaff83d..d0f89fe3 100644 --- a/assets/nuget-package-readme.md +++ b/assets/nuget-package-readme.md @@ -1,6 +1,6 @@ # TaskSeq -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. +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. This readme covers the highlights and a summary of implemented functions. A more extensive overview can be found in the [repository's readme][1]. @@ -30,7 +30,7 @@ A more extensive overview can be found in the [repository's readme][1]. ## Overview -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]. +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>`][5] given by a call to [`GetAsyncEnumerator()`][6]. 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`. @@ -125,10 +125,15 @@ This is what has been implemented so far, is planned or skipped: | ✅ [#11][] | | `collectSeq` | `collectSeqAsync` | | | | `compareWith` | `compareWith` | `compareWithAsync` | | | ✅ [#69][] | `concat` | `concat` | | | +| ✅ [#237][]| `concat` (list) | `concat` (list) | | | +| ✅ [#237][]| `concat` (array) | `concat` (array) | | | +| ✅ [#237][]| `concat` (r-array) | `concat` (r-array) | | | +| ✅ [#237][]| `concat` (seq) | `concat` (seq) | | | | ✅ [#70][] | `contains` | `contains` | | | | ✅ [#82][] | `delay` | `delay` | | | | | `distinct` | `distinct` | | | | | `distinctBy` | `dictinctBy` | `distinctByAsync` | | +| ✅ [#209][]| | `drop` | | | | ✅ [#2][] | `empty` | `empty` | | | | ✅ [#23][] | `exactlyOne` | `exactlyOne` | | | | ✅ [#83][] | `except` | `except` | | | @@ -144,15 +149,15 @@ This is what has been implemented so far, is planned or skipped: | | `fold2` | `fold2` | `fold2Async` | | | 🚫 | `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.") | | 🚫 | `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.") | -| | `forall` | `forall` | `forallAsync` | | +| ✅ [#240][]| `forall` | `forall` | `forallAsync` | | | | `forall2` | `forall2` | `forall2Async` | | | ❓ | `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.") | | ✅ [#23][] | `head` | `head` | | | | ✅ [#68][] | `indexed` | `indexed` | | | | ✅ [#69][] | `init` | `init` | `initAsync` | | | ✅ [#69][] | `initInfinite` | `initInfinite` | `initInfiniteAsync` | | -| | `insertAt` | `insertAt` | | | -| | `insertManyAt` | `insertManyAt` | | | +| ✅ [#236][]| `insertAt` | `insertAt` | | | +| ✅ [#236][]| `insertManyAt` | `insertManyAt` | | | | ✅ [#23][] | `isEmpty` | `isEmpty` | | | | ✅ [#23][] | `item` | `item` | | | | ✅ [#2][] | `iter` | `iter` | `iterAsync` | | @@ -190,15 +195,14 @@ This is what has been implemented so far, is planned or skipped: | 🚫 | `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.") | | | `reduce` | `reduce` | `reduceAsync` | | | 🚫 | `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.") | -| | `removeAt` | `removeAt` | | | -| | `removeManyAt` | `removeManyAt` | | | +| ✅ [#236][]| `removeAt` | `removeAt` | | | +| ✅ [#236][]| `removeManyAt` | `removeManyAt` | | | | | `replicate` | `replicate` | | | | ❓ | `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.") | | | `scan` | `scan` | `scanAsync` | | | 🚫 | `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.") | | ✅ [#90][] | `singleton` | `singleton` | | | | ✅ [#209][]| `skip` | `skip` | | | -| ✅ [#209][]| | `drop` | | | | ✅ [#219][]| `skipWhile` | `skipWhile` | `skipWhileAsync` | | | ✅ [#219][]| | `skipWhileInclusive` | `skipWhileInclusiveAsync` | | | ❓ | `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.") | @@ -232,7 +236,7 @@ This is what has been implemented so far, is planned or skipped: | ✅ [#23][] | `tryPick` | `tryPick` | `tryPickAsync` | | | ✅ [#76][] | | `tryTail` | | | | | `unfold` | `unfold` | `unfoldAsync` | | -| | `updateAt` | `updateAt` | | | +| ✅ [#236][]| `updateAt` | `updateAt` | | | | ✅ [#217][]| `where` | `where` | `whereAsync` | | | | `windowed` | `windowed` | | | | ✅ [#2][] | `zip` | `zip` | | | @@ -306,7 +310,13 @@ _The motivation for `readOnly` in `Seq` is that a cast from a mutable array or l [#83]: https://github.com/fsprojects/FSharp.Control.TaskSeq/pull/83 [#90]: https://github.com/fsprojects/FSharp.Control.TaskSeq/pull/90 [#126]: https://github.com/fsprojects/FSharp.Control.TaskSeq/pull/126 +[#133]: https://github.com/fsprojects/FSharp.Control.TaskSeq/issues/133 +[#167]: https://github.com/fsprojects/FSharp.Control.TaskSeq/issues/167 [#209]: https://github.com/fsprojects/FSharp.Control.TaskSeq/issues/209 [#217]: https://github.com/fsprojects/FSharp.Control.TaskSeq/issues/217 [#219]: https://github.com/fsprojects/FSharp.Control.TaskSeq/issues/219 [#221]: https://github.com/fsprojects/FSharp.Control.TaskSeq/issues/221 +[#237]: https://github.com/fsprojects/FSharp.Control.TaskSeq/issues/237 +[#236]: https://github.com/fsprojects/FSharp.Control.TaskSeq/issues/236 +[#240]: https://github.com/fsprojects/FSharp.Control.TaskSeq/issues/240 + diff --git a/release-notes.txt b/release-notes.txt index df20d858..7f0d630c 100644 --- a/release-notes.txt +++ b/release-notes.txt @@ -1,38 +1,43 @@ Release notes: -0.4.x (unreleased) - - overhaul all doc comments, add exceptions, improve IDE quick-info experience, #136 +0.4.0 + - overhaul all doc comments, add exceptions, improve IDE quick-info experience, #136, #220, #234 - new surface area functions, fixes #208: * TaskSeq.take, skip, #209 * TaskSeq.truncate, drop, #209 * TaskSeq.where, whereAsync, #217 * TaskSeq.skipWhile, skipWhileInclusive, skipWhileAsync, skipWhileInclusiveAsync, #219 * TaskSeq.max, min, maxBy, minBy, maxByAsync, minByAsync, #221 + * TaskSeq.insertAt, insertManyAt, removeAt, removeManyAt, updateAt, #236 + * TaskSeq.forall, forallAsync, #240 + * TaskSeq.concat (overloads: seq, array, resizearray, list), #237 - Performance: less thread hops with 'StartImmediateAsTask' instead of 'StartAsTask', fixes #135 - - BINARY INCOMPATIBILITY: 'TaskSeq' module is now static members on 'TaskSeq<_>', fixes #184 + - Performance: several inline and allocation improvements + - BINARY INCOMPATIBILITY: 'TaskSeq' module replaced by static members on 'TaskSeq<_>', fixes #184 - DEPRECATIONS (warning FS0044): - type 'taskSeq<_>' is renamed to 'TaskSeq<_>', fixes #193 - function 'ValueTask.ofIValueTaskSource` renamed to `ValueTask.ofSource`, fixes #193 - function `ValueTask.FromResult` is renamed to `ValueTask.fromResult`, fixes #193 0.4.0-alpha.1 - - fixes not calling Dispose for 'use!', 'use', or `finally` blocks #157 (by @bartelink) + - bugfix: not calling Dispose for 'use!', 'use', or `finally` blocks #157 (by @bartelink) - BREAKING CHANGE: null args now raise ArgumentNullException instead of NullReferenceException, #127 - adds `let!` and `do!` support for F#'s Async<'T>, #79, #114 - adds TaskSeq.takeWhile, takeWhileAsync, takeWhileInclusive, takeWhileInclusiveAsync, #126 (by @bartelink) - adds AsyncSeq vs TaskSeq comparison chart, #131 - - removes release-notes.txt from file dependencies, but keep in the package, #138 + - bugfix: removes release-notes.txt from file dependencies, but keep in the package, #138 0.3.0 - - internal renames, improved doc comments, signature files for complex types, hide internal-only types, fixes #112. + - improved xml doc comments, signature files for exposing types, fixes #112. - adds support for static TaskLike, allowing the same let! and do! overloads that F# task supports, fixes #110. - implements 'do!' for non-generic Task like with Task.Delay, fixes #43. - - adds support for 'for .. in ..' with task sequences in F# tasks and async, #75, #93 and #99 (with help from @theangrybyrd). + - task and async CEs extended with support for 'for .. in ..do' with TaskSeq, #75, #93, #99 (in part by @theangrybyrd). - adds TaskSeq.singleton, #90 (by @gusty). - - fixes overload resolution bug with 'use' and 'use!', #97 (thanks @peterfaria). + - bugfix: fixes overload resolution bug with 'use' and 'use!', #97 (thanks @peterfaria). - improves TaskSeq.empty by not relying on resumable state, #89 (by @gusty). - - does not throw exceptions anymore for unequal lengths in TaskSeq.zip, fixes #32. + - bugfix: does not throw exceptions anymore for unequal lengths in TaskSeq.zip, fixes #32. + - BACKWARD INCOMPATIBILITY: several internal-only types now hidden 0.2.2 - removes TaskSeq.toSeqCachedAsync, which was incorrectly named. Use toSeq or toListAsync instead. diff --git a/src/FSharp.Control.TaskSeq/FSharp.Control.TaskSeq.fsproj b/src/FSharp.Control.TaskSeq/FSharp.Control.TaskSeq.fsproj index 297af81f..15cfdc69 100644 --- a/src/FSharp.Control.TaskSeq/FSharp.Control.TaskSeq.fsproj +++ b/src/FSharp.Control.TaskSeq/FSharp.Control.TaskSeq.fsproj @@ -13,7 +13,7 @@ The 'taskSeq' computation expression adds support for awaitable asynchronous sequences with similar ease of use and performance to F#'s 'task' CE, with minimal overhead through ValueTask under the hood. TaskSeq brings 'seq' and 'task' together in a safe way. Generates optimized IL code through resumable state machines, and comes with a comprehensive set of functions in module 'TaskSeq'. See README for documentation and more info. - Copyright 2023 + Copyright 2022-2024 https://github.com/fsprojects/FSharp.Control.TaskSeq https://github.com/fsprojects/FSharp.Control.TaskSeq taskseq-icon.png @@ -22,7 +22,7 @@ Generates optimized IL code through resumable state machines, and comes with a c False nuget-package-readme.md $([System.IO.File]::ReadAllText("$(MSBuildProjectDirectory)/../../release-notes.txt")) - taskseq;f#;computation expression;IAsyncEnumerable;task;async;asyncseq; + taskseq;f#;fsharp;asyncseq;seq;sequences;sequential;threading;computation expression;IAsyncEnumerable;task;async;iteration True snupkg