An implementation of IAsyncEnumerable<'T>
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.4.0 is on NuGet.
See Releases for the an extensive version history of TaskSeq
. See Status overview below for a progress report.
- Overview
- Choosing between
AsyncSeq
andTaskSeq
- Status & planning
- More information
- Building & testing
- Work in progress
- Current set of
TaskSeq
utility functions
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
call on the IAsyncEnumerator<'T>
given by a call to GetAsyncEnumerator()
.
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
.
As with seq
and Seq
, this library comes with a bunch of well-known collection functions, like TaskSeq.empty
, isEmpty
or TaskSeq.map
, iter
, collect
, fold
and TaskSeq.find
, pick
, choose
, filter
, takeWhile
. Where applicable, these come with async variants, like TaskSeq.mapAsync
iterAsync
, collectAsync
, foldAsync
and TaskSeq.findAsync
, pickAsync
, chooseAsync
, filterAsync
, takeWhileAsync
which allows the applied function to be asynchronous.
See below for a full list of currently implemented functions and their variants.
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:
Dotnet Nuget
dotnet add package FSharp.Control.TaskSeq
For a specific project:
dotnet add myproject.fsproj package FSharp.Control.TaskSeq
F# Interactive (FSI):
// latest version
> #r "nuget: FSharp.Control.TaskSeq"
// or with specific version
> #r "nuget: FSharp.Control.TaskSeq, 0.4.0"
Paket:
dotnet paket add FSharp.Control.TaskSeq --project <project>
Package Manager:
PM> NuGet\Install-Package FSharp.Control.TaskSeq
As package reference in fsproj
or csproj
file:
<!-- replace version with most recent version -->
<PackageReference Include="FSharp.Control.TaskSeq" Version="0.4.0" />
open System.IO
open FSharp.Control
// singleton is fine
let helloTs = taskSeq { yield "Hello, World!" }
// cold-started, that is, delay-executed
let f() = task {
// using toList forces execution of whole sequence
let! hello = TaskSeq.toList helloTs // toList returns a Task<'T list>
return List.head hello
}
// can be mixed with normal sequences
let oneToTen = taskSeq { yield! [1..10] }
// can be used with F#'s task and async in a for-loop
let f() = task { for x in oneToTen do printfn "Number %i" x }
let g() = async { for x in oneToTen do printfn "Number %i" x }
// returns a delayed sequence of IAsyncEnumerable<string>
let allFilesAsLines() = taskSeq {
let files = Directory.EnumerateFiles(@"c:\temp")
for file in files do
// await
let! contents = File.ReadAllLinesAsync file
// return all lines
yield! contents
}
let write file =
allFilesAsLines()
// synchronous map function on asynchronous task sequence
|> TaskSeq.map (fun x -> x.Replace("a", "b"))
// asynchronous map
|> TaskSeq.mapAsync (fun x -> task { return "hello: " + x })
// asynchronous iter
|> TaskSeq.iterAsync (fun data -> File.WriteAllTextAsync(fileName, data))
// infinite sequence
let feedFromTwitter user pwd = taskSeq {
do! loginToTwitterAsync(user, pwd)
while true do
let! message = getNextNextTwitterMessageAsync()
yield message
}
The AsyncSeq
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+ |
F# concept of | task |
async |
Underlying type | Generic.IAsyncEnumerable<'T> note #1 |
Its own type, also called IAsyncEnumerable<'T> note #1 |
Implementation | State machine (statically compiled) | No state machine, continuation style |
Semantics | seq -like: on-demand |
seq -like: on-demand |
Disposability | Asynchronous, through IAsyncDisposable |
Synchronous, through IDisposable |
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> (= TaskSeq ), AsyncSeq , any sequence |
AsyncSeq |
Support for |
IAsyncEnumerable<'T> (= TaskSeq ), 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 |
Iteration semantics | Two operations, 'Next' is a value task, 'Current' must be called separately | One operation, 'Next' is Async , returns option with 'Current' |
MoveNextAsync |
Returns ValueTask<bool> |
Returns Async<'T option> |
Current |
Returns 'T |
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 | 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.
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:
- Take existing
taskSeq
resumable code from F# and fix it. DONE - Add almost all functions from
Seq
that could apply toTaskSeq
(full overview below). MOSTLY DONE, STILL TODO - Add remaining relevant functions from
Seq
. PLANNED FOR 0.4.x-
min
/max
/minBy
/maxBy
& async variant (see #221) -
insertAt
/updateAt
and related (see #236) -
average
/averageBy
,sum
and related -
forall
/forallAsync
(see #240) -
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
-
- Publish package on Nuget, DONE, PUBLISHED SINCE: 7 November 2022. See https://www.nuget.org/packages/FSharp.Control.TaskSeq
- Make
TaskSeq
interoperable withTask
by expanding the latter with afor .. in .. do
that acceps task sequences - Add to/from functions to seq, list, array
- Add applicable functions from
AsyncSeq
. PLANNED FOR 0.5-alpha - (Better) support for cancellations
- Interop with
AsyncSeq
. - (maybe) Support any awaitable type in the function lib (that is: where a
Task
is required, accept aValueTask
andAsync
as well) - Add
TaskEx
functionality (separate lib). DISCUSSION - Move documentation to https://fsprojects.github.io
- As of 9 November 2022: Nuget package available. In this phase, we will frequently update the package, see release notes.txt. Current version:
- Major update: 17 March 2024, version 0.4.0
The resumable state machine backing the taskSeq
CE is now finished and restartability (not to be confused with resumability) has been implemented and stabilized. Full support for empty task sequences is done. Focus is now on adding functionality there, like adding more useful overloads for yield
and let!
. Suggestions are welcome!.
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.
This is what has been implemented so far, is planned or skipped:
Done | Seq |
TaskSeq |
Variants | Remarks |
---|---|---|---|---|
❓ | allPairs |
allPairs |
note #1 | |
✅ #81 | append |
append |
||
✅ #81 | appendSeq |
|||
✅ #81 | prependSeq |
|||
average |
average |
|||
averageBy |
averageBy |
averageByAsync |
||
❓ | cache |
cache |
note #1 | |
✅ #67 | cast |
cast |
||
✅ #67 | box |
|||
✅ #67 | unbox |
|||
✅ #23 | choose |
choose |
chooseAsync |
|
chunkBySize |
chunkBySize |
|||
✅ #11 | collect |
collect |
collectAsync |
|
✅ #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 |
||
✅ #83 | exceptOfSeq |
|||
✅ #70 | exists |
exists |
existsAsync |
|
exists2 |
exists2 |
|||
✅ #23 | filter |
filter |
filterAsync |
|
✅ #23 | find |
find |
findAsync |
|
🚫 | findBack |
note #2 | ||
✅ #68 | findIndex |
findIndex |
findIndexAsync |
|
🚫 | findIndexBack |
n/a | n/a | note #2 |
✅ #2 | fold |
fold |
foldAsync |
|
fold2 |
fold2 |
fold2Async |
||
🚫 | foldBack |
note #2 | ||
🚫 | foldBack2 |
note #2 | ||
✅ #240 | forall |
forall |
forallAsync |
|
forall2 |
forall2 |
forall2Async |
||
❓ | groupBy |
groupBy |
groupByAsync |
note #1 |
✅ #23 | head |
head |
||
✅ #68 | indexed |
indexed |
||
✅ #69 | init |
init |
initAsync |
|
✅ #69 | initInfinite |
initInfinite |
initInfiniteAsync |
|
✅ #236 | insertAt |
insertAt |
||
✅ #236 | insertManyAt |
insertManyAt |
||
✅ #23 | isEmpty |
isEmpty |
||
✅ #23 | item |
item |
||
✅ #2 | iter |
iter |
iterAsync |
|
iter2 |
iter2 |
iter2Async |
||
✅ #2 | iteri |
iteri |
iteriAsync |
|
iteri2 |
iteri2 |
iteri2Async |
||
✅ #23 | last |
last |
||
✅ #53 | length |
length |
||
✅ #53 | lengthBy |
lengthByAsync |
||
✅ #2 | map |
map |
mapAsync |
|
map2 |
map2 |
map2Async |
||
map3 |
map3 |
map3Async |
||
mapFold |
mapFold |
mapFoldAsync |
||
🚫 | mapFoldBack |
note #2 | ||
✅ #2 | mapi |
mapi |
mapiAsync |
|
mapi2 |
mapi2 |
mapi2Async |
||
✅ #221 | max |
max |
||
✅ #221 | maxBy |
maxBy |
maxByAsync |
|
✅ #221 | min |
min |
||
✅ #221 | minBy |
minBy |
minByAsync |
|
✅ #2 | ofArray |
ofArray |
||
✅ #2 | ofAsyncArray |
|||
✅ #2 | ofAsyncList |
|||
✅ #2 | ofAsyncSeq |
|||
✅ #2 | ofList |
ofList |
||
✅ #2 | ofTaskList |
|||
✅ #2 | ofResizeArray |
|||
✅ #2 | ofSeq |
|||
✅ #2 | ofTaskArray |
|||
✅ #2 | ofTaskList |
|||
✅ #2 | ofTaskSeq |
|||
pairwise |
pairwise |
|||
permute |
permute |
permuteAsync |
||
✅ #23 | pick |
pick |
pickAsync |
|
🚫 | readOnly |
note #3 | ||
reduce |
reduce |
reduceAsync |
||
🚫 | reduceBack |
note #2 | ||
✅ #236 | removeAt |
removeAt |
||
✅ #236 | removeManyAt |
removeManyAt |
||
replicate |
replicate |
|||
❓ | rev |
note #1 | ||
scan |
scan |
scanAsync |
||
🚫 | scanBack |
note #2 | ||
✅ #90 | singleton |
singleton |
||
✅ #209 | skip |
skip |
||
✅ #219 | skipWhile |
skipWhile |
skipWhileAsync |
|
✅ #219 | skipWhileInclusive |
skipWhileInclusiveAsync |
||
❓ | sort |
note #1 | ||
❓ | sortBy |
note #1 | ||
❓ | sortByAscending |
note #1 | ||
❓ | sortByDescending |
note #1 | ||
❓ | sortWith |
note #1 | ||
splitInto |
splitInto |
|||
sum |
sum |
|||
sumBy |
sumBy |
sumByAsync |
||
✅ #76 | tail |
tail |
||
✅ #209 | take |
take |
||
✅ #126 | takeWhile |
takeWhile |
takeWhileAsync |
|
✅ #126 | takeWhileInclusive |
takeWhileInclusiveAsync |
||
✅ #2 | toArray |
toArray |
toArrayAsync |
|
✅ #2 | toIList |
toIListAsync |
||
✅ #2 | toList |
toList |
toListAsync |
|
✅ #2 | toResizeArray |
toResizeArrayAsync |
||
✅ #2 | toSeq |
toSeqAsync |
||
[…] | ||||
❓ | transpose |
note #1 | ||
✅ #209 | truncate |
truncate |
||
✅ #23 | tryExactlyOne |
tryExactlyOne |
tryExactlyOneAsync |
|
✅ #23 | tryFind |
tryFind |
tryFindAsync |
|
🚫 | tryFindBack |
note #2 | ||
✅ #68 | tryFindIndex |
tryFindIndex |
tryFindIndexAsync |
|
🚫 | tryFindIndexBack |
note #2 | ||
✅ #23 | tryHead |
tryHead |
||
✅ #23 | tryItem |
tryItem |
||
✅ #23 | tryLast |
tryLast |
||
✅ #23 | tryPick |
tryPick |
tryPickAsync |
|
✅ #76 | tryTail |
|||
unfold |
unfold |
unfoldAsync |
||
✅ #236 | updateAt |
updateAt |
||
✅ #217 | where |
where |
whereAsync |
|
windowed |
windowed |
|||
✅ #2 | zip |
zip |
||
zip3 |
zip3 |
|||
zip4 |
¹⁾ 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.
²⁾ 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 xxxBack
iterators.
³⁾ 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.
- A good C#-based introduction can be found in this blog.
- An MSDN article written shortly after it was introduced.
- Converting a
seq
to anIAsyncEnumerable
demo gist as an example, thoughTaskSeq
contains many more utility functions and uses a slightly different approach.
- A state machine from a monadic perspective in F# can be found here, which works with the pre-F# 6.0 non-resumable internals.
- The original RFC for F# 6.0 on resumable state machines
- The original RFC for introducing
task
to F# 6.0. - A pre F# 6.0
TaskBuilder
that motivated thetask
CE later added to F# Core. - MSDN Documentation on
task
andasync
.
- Docs on MSDN form a good summary and starting point.
- Arguably the best step-by-step tutorial to using and building computation expressions by Scott Wlaschin.
TLDR: just run build
. Or load the sln
file in Visual Studio or VS Code and compile.
At the very least, to get the source to compile, you'll need:
- .NET 6 or .NET 7 Preview
- F# 6.0 or 7.0 compiler
- To use
build.cmd
, thedotnet
command must be accessible from your path.
Just check-out this repo locally. Then, from the root of the repo, you can do:
build [build] [release|debug]
With no arguments, defaults to release
.
build test [release|debug]
With no arguments, defaults to release
. By default, all tests are output to the console. If you don't want that, you can use --logger console;verbosity=summary
.
Furthermore, no TRX file is generated and the --blame-xxx
flags aren't set.
build ci [release|debug]
With no arguments, defaults to release
. This will run dotnet test
with the --blame-xxx
settings enabled to prevent hanging tests caused by
an xUnit runner bug.
There are no special CI environment variables that need to be set for running this locally.
You can pass any additional options that are valid for dotnet test
and dotnet build
respectively. However,
these cannot be the very first argument, so you should either use build build --myadditionalOptions fizz buzz
, or
just specify the build-kind, i.e. this is fine:
build debug --verbosity detailed
build test --logger console;verbosity=summary
At this moment, additional options cannot have quotes in them.
Command modifiers, like release
and debug
, can be specified with -
or /
if you so prefer: dotnet build /release
.
build help
For more info, see this PR: #29.
The taskSeq
CE using the statically compilable resumable state machine approach is based on, and draw heavily from Don Symes taskSeq.fs
as used to test the resumable state machine in the F# core compiler.
On top of that, this library adds a set of TaskSeq
module functions, with their Async
variants, on par with Seq
and AsyncSeq
.
The following are the current surface area of the TaskSeq
utility functions, ordered alphabetically.
module TaskSeq =
val append: source1: TaskSeq<'T> -> source2: TaskSeq<'T> -> TaskSeq<'T>
val appendSeq: source1: TaskSeq<'T> -> source2: seq<'T> -> TaskSeq<'T>
val box: source: TaskSeq<'T> -> TaskSeq<obj>
val cast: source: TaskSeq<obj> -> TaskSeq<'T>
val choose: chooser: ('T -> 'U option) -> source: TaskSeq<'T> -> TaskSeq<'U>
val chooseAsync: chooser: ('T -> #Task<'U option>) -> source: TaskSeq<'T> -> TaskSeq<'U>
val collect: binder: ('T -> #TaskSeq<'U>) -> source: TaskSeq<'T> -> TaskSeq<'U>
val collectAsync: binder: ('T -> #Task<'TSeqU>) -> source: TaskSeq<'T> -> TaskSeq<'U> when 'TSeqU :> TaskSeq<'U>
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<ResizeArray<'T>> -> TaskSeq<'T>
val contains<'T when 'T: equality> : value: 'T -> source: TaskSeq<'T> -> Task<bool>
val delay: generator: (unit -> TaskSeq<'T>) -> TaskSeq<'T>
val drop: count: int -> source: TaskSeq<'T> -> TaskSeq<'T>
val empty<'T> : TaskSeq<'T>
val exactlyOne: source: TaskSeq<'T> -> Task<'T>
val except<'T when 'T: equality> : itemsToExclude: TaskSeq<'T> -> source: TaskSeq<'T> -> TaskSeq<'T>
val exceptOfSeq<'T when 'T: equality> : itemsToExclude: seq<'T> -> source: TaskSeq<'T> -> TaskSeq<'T>
val exists: predicate: ('T -> bool) -> source: TaskSeq<'T> -> Task<bool>
val existsAsync: predicate: ('T -> #Task<bool>) -> source: TaskSeq<'T> -> Task<bool>
val filter: predicate: ('T -> bool) -> source: TaskSeq<'T> -> TaskSeq<'T>
val filterAsync: predicate: ('T -> #Task<bool>) -> source: TaskSeq<'T> -> TaskSeq<'T>
val find: predicate: ('T -> bool) -> source: TaskSeq<'T> -> Task<'T>
val findAsync: predicate: ('T -> #Task<bool>) -> source: TaskSeq<'T> -> Task<'T>
val findIndex: predicate: ('T -> bool) -> source: TaskSeq<'T> -> Task<int>
val findIndexAsync: predicate: ('T -> #Task<bool>) -> source: TaskSeq<'T> -> Task<int>
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<bool>
val forallAsync: predicate: ('T -> #Task<bool>) -> source: TaskSeq<'T> -> Task<bool>
val head: source: TaskSeq<'T> -> Task<'T>
val indexed: source: TaskSeq<'T> -> TaskSeq<int * 'T>
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<bool>
val item: index: int -> source: TaskSeq<'T> -> Task<'T>
val iter: action: ('T -> unit) -> source: TaskSeq<'T> -> Task<unit>
val iterAsync: action: ('T -> #Task<unit>) -> source: TaskSeq<'T> -> Task<unit>
val iteri: action: (int -> 'T -> unit) -> source: TaskSeq<'T> -> Task<unit>
val iteriAsync: action: (int -> 'T -> #Task<unit>) -> source: TaskSeq<'T> -> Task<unit>
val last: source: TaskSeq<'T> -> Task<'T>
val length: source: TaskSeq<'T> -> Task<int>
val lengthBy: predicate: ('T -> bool) -> source: TaskSeq<'T> -> Task<int>
val lengthByAsync: predicate: ('T -> #Task<bool>) -> source: TaskSeq<'T> -> Task<int>
val lengthOrMax: max: int -> source: TaskSeq<'T> -> Task<int>
val map: mapper: ('T -> 'U) -> source: TaskSeq<'T> -> TaskSeq<'U>
val mapAsync: mapper: ('T -> #Task<'U>) -> source: TaskSeq<'T> -> TaskSeq<'U>
val mapi: mapper: (int -> 'T -> 'U) -> source: TaskSeq<'T> -> TaskSeq<'U>
val mapiAsync: mapper: (int -> 'T -> #Task<'U>) -> source: TaskSeq<'T> -> TaskSeq<'U>
val max: source: TaskSeq<'T> -> Task<'T> when 'T: comparison
val max: source: TaskSeq<'T> -> Task<'T> when 'T: comparison
val maxBy: projection: ('T -> 'U) -> source: TaskSeq<'T> -> Task<'T> when 'U: comparison
val minBy: projection: ('T -> 'U) -> source: TaskSeq<'T> -> Task<'T> when 'U: comparison
val maxByAsync: projection: ('T -> #Task<'U>) -> source: TaskSeq<'T> -> Task<'T> when 'U: comparison
val minByAsync: projection: ('T -> #Task<'U>) -> source: TaskSeq<'T> -> Task<'T> when 'U: comparison val ofArray: source: 'T[] -> TaskSeq<'T>
val ofAsyncArray: source: Async<'T> array -> TaskSeq<'T>
val ofAsyncList: source: Async<'T> list -> TaskSeq<'T>
val ofAsyncSeq: source: seq<Async<'T>> -> TaskSeq<'T>
val ofList: source: 'T list -> TaskSeq<'T>
val ofResizeArray: source: ResizeArray<'T> -> TaskSeq<'T>
val ofSeq: source: seq<'T> -> TaskSeq<'T>
val ofTaskArray: source: #Task<'T> array -> TaskSeq<'T>
val ofTaskList: source: #Task<'T> list -> TaskSeq<'T>
val ofTaskSeq: source: seq<#Task<'T>> -> TaskSeq<'T>
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<TaskSeq<'T>>
val take: count: int -> source: TaskSeq<'T> -> 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>>
val toList: source: TaskSeq<'T> -> 'T list
val toListAsync: source: TaskSeq<'T> -> Task<'T list>
val toResizeArrayAsync: source: TaskSeq<'T> -> Task<ResizeArray<'T>>
val toSeq: source: TaskSeq<'T> -> seq<'T>
val truncate: count: int -> source: TaskSeq<'T> -> TaskSeq<'T>
val tryExactlyOne: source: TaskSeq<'T> -> Task<'T option>
val tryFind: predicate: ('T -> bool) -> source: TaskSeq<'T> -> Task<'T option>
val tryFindAsync: predicate: ('T -> #Task<bool>) -> source: TaskSeq<'T> -> Task<'T option>
val tryFindIndex: predicate: ('T -> bool) -> source: TaskSeq<'T> -> Task<int option>
val tryFindIndexAsync: predicate: ('T -> #Task<bool>) -> source: TaskSeq<'T> -> Task<int option>
val tryHead: source: TaskSeq<'T> -> Task<'T option>
val tryItem: index: int -> source: TaskSeq<'T> -> Task<'T option>
val tryLast: source: TaskSeq<'T> -> Task<'T option>
val tryPick: chooser: ('T -> 'U option) -> source: TaskSeq<'T> -> Task<'U option>
val tryPickAsync: chooser: ('T -> #Task<'U option>) -> source: TaskSeq<'T> -> Task<'U option>
val tryTail: source: TaskSeq<'T> -> Task<TaskSeq<'T> option>
val where: predicate: ('T -> bool) -> source: TaskSeq<'T> -> TaskSeq<'T>
val whereAsync: predicate: ('T -> #Task<bool>) -> source: TaskSeq<'T> -> TaskSeq<'T>
val unbox<'U when 'U: struct> : source: TaskSeq<obj> -> TaskSeq<'U>
val updateAt: position:int -> value:'T -> source: TaskSeq<'T> -> TaskSeq<'T>
val zip: source1: TaskSeq<'T> -> source2: TaskSeq<'U> -> TaskSeq<'T * 'U>