diff --git a/.gitignore b/.gitignore index 72d94d66..7f29938d 100644 --- a/.gitignore +++ b/.gitignore @@ -146,9 +146,8 @@ _NCrunch_* .*crunch*.local.xml nCrunchTemp_* -# MightyMoose -*.mm.* -AutoTest.Net/ +# Rider / JetBrains IDEs +.idea/ # Web workbench (sass) .sass-cache/ diff --git a/README.md b/README.md index 950cbcdc..e3fa54d0 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ Latest version [can be installed from Nuget][nuget]. 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]. -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`. +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`. ### Module functions @@ -58,9 +58,12 @@ As with `seq` and `Seq`, this library comes with a bunch of well-known collectio ### `taskSeq` computation expressions -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 -looping over a normal or asynchronous sequence (one that implements `IAsyncEnumerable<'T>'`). You can use `yield!` and `yield` and there's support -for `use` and `use!`, `try-with` and `try-finally` and `while` loops within the task sequence expression: +The `taskSeq` computation expression can be used just like using `seq`. +Additionally, it adds support for working with `Task`s through `let!` and +looping over both normal and asynchronous sequences (ones that implement +`IAsyncEnumerable<'T>'`). You can use `yield!` and `yield` and there's support +for `use` and `use!`, `try-with` and `try-finally` and `while` loops within +the task sequence expression: ### Installation @@ -183,7 +186,7 @@ The _resumable state machine_ backing the `taskSeq` CE is now finished and _rest ### Progress and implemented `TaskSeq` module functions -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. +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 by `AsyncSeq`. Each implemented function is documented through XML doc comments to provide the necessary context-sensitive help. The following is the progress report: @@ -327,12 +330,17 @@ The following is the progress report: ## More information -### Further reading `IAsyncEnumerable` +### The AsyncSeq library + +If you're looking to use `IAsyncEnumerable` with `async` and not `task`, the existing [`AsyncSeq`][11] library already provides excellent coverage of that use case. While `TaskSeq` is intended to interoperate with `async` as `task` does, it's not intended to provide an `AsyncSeq` type (at least not yet). + +In short, if your application is using `Async` (and the parallelism features stemming from that), consider using the `AsyncSeq` library instead. + +### Further reading on `IAsyncEnumerable` - A good C#-based introduction [can be found in this blog][8]. - [An MSDN article][9] written shortly after it was introduced. - Converting a `seq` to an `IAsyncEnumerable` [demo gist][10] as an example, though `TaskSeq` contains many more utility functions and uses a slightly different approach. -- If you're looking for using `IAsyncEnumerable` with `async` and not `task`, the excellent [`AsyncSeq`][11] library should be used. While `TaskSeq` is intended to consume `async` just like `task` does, it won't create an `AsyncSeq` type (at least not yet). If you want classic Async and parallelism, you should get this library instead. ### Further reading on resumable state machines @@ -487,6 +495,10 @@ module TaskSeq = val prependSeq: source1: #seq<'T> -> source2: #taskSeq<'T> -> taskSeq<'T> val singleton: source: 'T -> taskSeq<'T> val tail: source: taskSeq<'T> -> Task> + val takeWhile: predicate: ('T -> bool) -> source: taskSeq<'T> -> Task> + val takeWhileAsync: predicate: ('T -> #Task) -> source: taskSeq<'T> -> Task> + val takeWhileInclusive: predicate: ('T -> bool) -> source: taskSeq<'T> -> Task> + val takeWhileInclusiveAsync: predicate: ('T -> #Task) -> source: taskSeq<'T> -> Task> val toArray: source: taskSeq<'T> -> 'T[] val toArrayAsync: source: taskSeq<'T> -> Task<'T[]> val toIListAsync: source: taskSeq<'T> -> Task> diff --git a/src/FSharp.Control.TaskSeq.SmokeTests/FSharp.Control.TaskSeq.SmokeTests.fsproj b/src/FSharp.Control.TaskSeq.SmokeTests/FSharp.Control.TaskSeq.SmokeTests.fsproj index 4ef3120b..9acf172c 100644 --- a/src/FSharp.Control.TaskSeq.SmokeTests/FSharp.Control.TaskSeq.SmokeTests.fsproj +++ b/src/FSharp.Control.TaskSeq.SmokeTests/FSharp.Control.TaskSeq.SmokeTests.fsproj @@ -2,9 +2,6 @@ net6.0 - - false - false @@ -13,7 +10,6 @@ - diff --git a/src/FSharp.Control.TaskSeq.SmokeTests/Program.fs b/src/FSharp.Control.TaskSeq.SmokeTests/Program.fs deleted file mode 100644 index 0695f84c..00000000 --- a/src/FSharp.Control.TaskSeq.SmokeTests/Program.fs +++ /dev/null @@ -1 +0,0 @@ -module Program = let [] main _ = 0 diff --git a/src/FSharp.Control.TaskSeq.Test/FSharp.Control.TaskSeq.Test.fsproj b/src/FSharp.Control.TaskSeq.Test/FSharp.Control.TaskSeq.Test.fsproj index 037acb8f..7d44b1ee 100644 --- a/src/FSharp.Control.TaskSeq.Test/FSharp.Control.TaskSeq.Test.fsproj +++ b/src/FSharp.Control.TaskSeq.Test/FSharp.Control.TaskSeq.Test.fsproj @@ -49,7 +49,6 @@ - diff --git a/src/FSharp.Control.TaskSeq.Test/Nunit.Extensions.fs b/src/FSharp.Control.TaskSeq.Test/Nunit.Extensions.fs index 785fcc8f..8c815e69 100644 --- a/src/FSharp.Control.TaskSeq.Test/Nunit.Extensions.fs +++ b/src/FSharp.Control.TaskSeq.Test/Nunit.Extensions.fs @@ -104,7 +104,7 @@ module ExtraCustomMatchers = /// Asserts any exception that matches, or is derived from the given exception . /// Async exceptions are almost always nested in an , however, in an /// async try/catch in F#, the exception is typically unwrapped. But this is not foolproof, and - /// in cases where we just call , and will be raised regardless. + /// in cases where we just call , an will be raised regardless. /// This assertion will go over all nested exceptions and 'self', to find a matching exception. /// Function to evaluate MUST return a , not a generic /// . diff --git a/src/FSharp.Control.TaskSeq.Test/Program.fs b/src/FSharp.Control.TaskSeq.Test/Program.fs deleted file mode 100644 index 80c6d842..00000000 --- a/src/FSharp.Control.TaskSeq.Test/Program.fs +++ /dev/null @@ -1,3 +0,0 @@ -module Program = - [] - let main _ = 0 diff --git a/src/FSharp.Control.TaskSeq/FSharp.Control.TaskSeq.fsproj b/src/FSharp.Control.TaskSeq/FSharp.Control.TaskSeq.fsproj index 460f78da..f3c99bdc 100644 --- a/src/FSharp.Control.TaskSeq/FSharp.Control.TaskSeq.fsproj +++ b/src/FSharp.Control.TaskSeq/FSharp.Control.TaskSeq.fsproj @@ -9,9 +9,9 @@ Abel Braaksma; Don Syme This library brings C#'s concept of 'await foreach' to F#, with a seamless implementation of IAsyncEnumerable<'T>. -The 'taskSeq' computation expression adds support for awaitable asyncronous sequences with a similar ease of use and performance as F#'s 'task' CE, with minimal overhead through ValueTask under the hood. TaskSeq brings 'seq' and 'task' together in a safe way. +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 the new resumable state machines, and comes with a comprehensive set of helpful functions in module 'TaskSeq'. See README for documentation and more info. +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 2022 https://github.com/fsprojects/FSharp.Control.TaskSeq https://github.com/fsprojects/FSharp.Control.TaskSeq