Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,8 @@ _NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*

# MightyMoose
*.mm.*
AutoTest.Net/
# Rider / JetBrains IDEs
.idea/

# Web workbench (sass)
.sass-cache/
Expand Down
26 changes: 19 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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

Expand Down Expand Up @@ -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:

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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<taskSeq<'T>>
val takeWhile: predicate: ('T -> bool) -> source: taskSeq<'T> -> Task<taskSeq<'T>>
val takeWhileAsync: predicate: ('T -> #Task<bool>) -> source: taskSeq<'T> -> Task<taskSeq<'T>>
val takeWhileInclusive: predicate: ('T -> bool) -> source: taskSeq<'T> -> Task<taskSeq<'T>>
val takeWhileInclusiveAsync: predicate: ('T -> #Task<bool>) -> source: taskSeq<'T> -> Task<taskSeq<'T>>
val toArray: source: taskSeq<'T> -> 'T[]
val toArrayAsync: source: taskSeq<'T> -> Task<'T[]>
val toIListAsync: source: taskSeq<'T> -> Task<IList<'T>>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>

<IsPackable>false</IsPackable>
<GenerateProgramFile>false</GenerateProgramFile>
</PropertyGroup>

<ItemGroup>
Expand All @@ -13,7 +10,6 @@

<ItemGroup>
<Compile Include="SmokeTests.fs" />
<Compile Include="Program.fs" />
</ItemGroup>

<ItemGroup>
Expand Down
1 change: 0 additions & 1 deletion src/FSharp.Control.TaskSeq.SmokeTests/Program.fs

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@
<Compile Include="TaskSeq.Do.Tests.fs" />
<Compile Include="TaskSeq.Let.Tests.fs" />
<Compile Include="TaskSeq.Using.Tests.fs" />
<Compile Include="Program.fs" />
</ItemGroup>

<ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion src/FSharp.Control.TaskSeq.Test/Nunit.Extensions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ module ExtraCustomMatchers =
/// Asserts any exception that matches, or is derived from the given exception <see cref="Type" />.
/// Async exceptions are almost always nested in an <see cref="AggregateException" />, 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 <see cref="Task.Wait" />, and <see cref="AggregateException" /> will be raised regardless.
/// in cases where we just call <see cref="Task.Wait" />, an <see cref="AggregateException" /> 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 <see cref="System.Threading.Tasks.Task" />, not a generic
/// <see cref="Task&lt;'T>" />.
Expand Down
3 changes: 0 additions & 3 deletions src/FSharp.Control.TaskSeq.Test/Program.fs

This file was deleted.

4 changes: 2 additions & 2 deletions src/FSharp.Control.TaskSeq/FSharp.Control.TaskSeq.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
<Authors>Abel Braaksma; Don Syme</Authors>
<Description>This library brings C#'s concept of 'await foreach' to F#, with a seamless implementation of IAsyncEnumerable&lt;'T&gt;.

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.</Description>
Generates optimized IL code through resumable state machines, and comes with a comprehensive set of helper functions in module 'TaskSeq'. See README for documentation and more info.</Description>
<Copyright>Copyright 2022</Copyright>
<PackageProjectUrl>https://github.com/fsprojects/FSharp.Control.TaskSeq</PackageProjectUrl>
<RepositoryUrl>https://github.com/fsprojects/FSharp.Control.TaskSeq</RepositoryUrl>
Expand Down