Skip to content

Add AsyncSeq vs TaskSeq comparison readme #131

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Dec 16, 2022
Merged
Show file tree
Hide file tree
Changes from all 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
47 changes: 36 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@

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.

Latest version [can be installed from Nuget][nuget].
Latest version [can be installed from Nuget][nuget].

## Release notes<!-- omit in toc -->

See [release notes.txt](release-notes.txt) for the version history of `TaskSeq`. See [Status overview](#status--planning) for current status of the surface area of `TaskSeq`.

-----------------------------------------

Expand All @@ -24,15 +28,16 @@ Latest version [can be installed from Nuget][nuget].
- [`taskSeq` computation expressions](#taskseq-computation-expressions)
- [Installation](#installation)
- [Examples](#examples)
- [Status & planning](#status--planning)
- [Choosing between `AsyncSeq` and `TaskSeq`](#choosing-between-asyncseq-and-taskseq)
- [Status \& planning](#status--planning)
- [Implementation progress](#implementation-progress)
- [Progress `taskSeq` CE](#progress-taskseq-ce)
- [Progress and implemented `TaskSeq` module functions](#progress-and-implemented-taskseq-module-functions)
- [More information](#more-information)
- [Further reading `IAsyncEnumerable`](#further-reading-iasyncenumerable)
- [Further reading on `IAsyncEnumerable`](#further-reading-on-iasyncenumerable)
- [Further reading on resumable state machines](#further-reading-on-resumable-state-machines)
- [Further reading on computation expressions](#further-reading-on-computation-expressions)
- [Building & testing](#building--testing)
- [Building \& testing](#building--testing)
- [Prerequisites](#prerequisites)
- [Build the solution](#build-the-solution)
- [Run the tests](#run-the-tests)
Expand Down Expand Up @@ -163,9 +168,35 @@ let feedFromTwitter user pwd = taskSeq {
}
```

## Choosing between `AsyncSeq` and `TaskSeq`

The [`AsyncSeq`][11] and `TaskSeq` libraries both operate on asynchronous sequences, but there are a few fundamental differences. The most notable being that the former _does not_ implement `IAsyncEnumerable<'T>`, though it does have a type of that name with different semantics (not surprising; it predates the definition of the modern one). Another key difference is that `TaskSeq` uses `ValueTask`s for the asynchronous computations, whereas `AsyncSeq` uses F#'s `Async<'T>`.

There are more differences:

| | `TaskSeq` | `AsyncSeq` |
|----------------------------|---------------------------------------------------------------------------------|----------------------------------------------------------------------|
| **Frameworks** | .NET 5.0+, NetStandard 2.1 | .NET 5.0+, NetStandard 2.0 and 2.1, .NET Framework 4.6.1+ |
| **Underlying type** | `System.Collections.Generic.IAsyncEnumerable<'T>` | Its own type, also called `IAsyncEnumerable<'T>`, but not compatible |
| **Implementation** | State machine (statically compiled) | No state machine, continuation style |
| **Semantics** | `seq`-like: on-demand | `seq`-like: on-demand |
| **Support `let!`** | All `task`-like: `Async<'T>`, `Task<'T>`, `ValueTask<'T>` or any `GetAwaiter()` | `Async<'T>` only |
| **Support `do!`** | `Async<unit>`, `Task<unit>` and `Task`, `ValueTask<unit>` and `ValueTask` | `Async<unit>` only |
| **Support `yield!`** | `IAsyncEnumerable<'T>`, `AsyncSeq`, any sequence | `AsyncSeq` |
| **Support `for`** | `IAsyncEnumerable<'T>`, `AsyncSeq`, any sequence | `AsyncSeq`, any sequence |
| **Behavior with `yield`** | Zero allocations; no `Task` or even `ValueTask` created | Allocates an F# `Async` wrapped in a singleton `AsyncSeq` |
| **Conversion to other** | `TaskSeq.toAsyncSeq` | `AsyncSeq.toAsyncEnum` |
| **Conversion from other** | Implicit (`yield!`) or `TaskSeq.ofAsyncSeq` | `AsyncSeq.ofAsyncEnum` |
| **Recursion in `yield!`** | **No** (requires F# support, upcoming) | Yes |
| **Based on F# concept of** | `task` | `async` |
| **`MoveNextAsync`** impl | `ValueTask<bool>` | `Async<'T option>` |
| **Cancellation** | Implicit token governs iteration | Implicit token flows to all subtasks per `async` semantics |
| **Performance** | Very high, negligible allocations | Slower, more allocations, due to using `async` |
| **Parallelism** | Possible with ChildTask; support will follow | Supported explicitly |

## 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 implmented functions below. Here's the short list:
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**
Expand Down Expand Up @@ -330,12 +361,6 @@ The following is the progress report:

## More information

### 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].
Expand Down
1 change: 1 addition & 0 deletions release-notes.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
Release notes:
0.4.x (unreleased)
- adds TaskSeq.takeWhile, takeWhileAsync, takeWhileInclusive, takeWhileInclusiveAsync, #126 (by @bartelink)
- adds AsyncSeq vs TaskSeq comparison chart, #131

0.3.0
- internal renames, improved doc comments, signature files for complex types, hide internal-only types, fixes #112.
Expand Down