Transforming and Filtering Sequences
This document covers the core operations for transforming and filtering AsyncSeq<'T> values:
map, mapAsync, filter, filterAsync, and mapFoldAsync.
For operations that consume a sequence into a single result, see Consuming Sequences.
open FSharp.Control
Transforming Sequences with computation expressions
The most general and simplest way to transform asynchronous sequences is to write a function that accepts an AsyncSeq<_> and returns an AsyncSeq<_> and is implemented using an asyncSeq { ... } computation expression. For example, the following function transforms a sequence of integers into a sequence of strings that labels each integer as even or odd:
let transform (input: AsyncSeq<int>) : AsyncSeq<string> =
asyncSeq {
for n in input do
if n % 2 = 0 then
do! Async.Sleep 100 // simulate some async work
yield sprintf "Even: %d" n
else
yield sprintf "Odd: %d" n
}
Here the for loop is an asynchronous loop that iterates over the input sequence, awaiting each element. On even numbers, it simulates some asynchronous work before yielding a result. On odd numbers, it yields immediately.
Inside asyncSeq { ... }, you can use any F# constructs such as loops, conditionals. You can also use let! or do! to await individual Async<_> values:
let transformWithAsync (input: AsyncSeq<int>) : AsyncSeq<string> =
asyncSeq {
for n in input do
let! isEven = async { return n % 2 = 0 } // simulate async check
if isEven then
yield sprintf "Even: %d" n
else
yield sprintf "Odd: %d" n
}
Using map and mapAsync
Instead of writing a full computation expression, you can use AsyncSeq.map to transform each element of a sequence synchronously:
let strings = asyncSeq { yield! [ "hello"; "world"; "asyncseq" ] }
let upperCased : AsyncSeq<string> =
strings |> AsyncSeq.map (fun s -> s.ToUpperInvariant())
AsyncSeq.mapAsync is the same but the projection returns Async<'U>, so it can perform asynchronous work per element — for example, fetching metadata for each item:
let fetchLength (url: string) : Async<int> =
async { return url.Length } // placeholder for a real HTTP call
let lengths : AsyncSeq<int> =
strings |> AsyncSeq.mapAsync fetchLength
Using filter and filterAsync
AsyncSeq.filter keeps only elements satisfying a synchronous predicate:
let longStrings : AsyncSeq<string> =
strings |> AsyncSeq.filter (fun s -> s.Length > 4)
AsyncSeq.filterAsync does the same with an asynchronous predicate — useful when the
keep/discard decision requires an async lookup:
let isInteresting (s: string) : Async<bool> =
async { return s.Contains('o') } // placeholder for a real async check
let interesting : AsyncSeq<string> =
strings |> AsyncSeq.filterAsync isInteresting
namespace FSharp
--------------------
namespace Microsoft.FSharp
namespace FSharp.Control
--------------------
namespace Microsoft.FSharp.Control
module AsyncSeq from FSharp.Control
--------------------
type AsyncSeq<'T> = System.Collections.Generic.IAsyncEnumerable<'T>
<summary> An asynchronous sequence; equivalent to System.Collections.Generic.IAsyncEnumerable<'T>. Use the asyncSeq { ... } computation expression to create values, and the AsyncSeq module for combinators. </summary>
val int: value: 'T -> int (requires member op_Explicit)
--------------------
type int = int32
--------------------
type int<'Measure> = int
val string: value: 'T -> string
--------------------
type string = System.String
<summary> Builds an asynchronous sequence using the computation builder syntax </summary>
type Async = static member AsBeginEnd: computation: ('Arg -> Async<'T>) -> ('Arg * AsyncCallback * objnull -> IAsyncResult) * (IAsyncResult -> 'T) * (IAsyncResult -> unit) static member AwaitEvent: event: IEvent<'Del,'T> * ?cancelAction: (unit -> unit) -> Async<'T> (requires delegate and 'Del :> Delegate) static member AwaitIAsyncResult: iar: IAsyncResult * ?millisecondsTimeout: int -> Async<bool> static member AwaitTask: task: Task<'T> -> Async<'T> + 1 overload static member AwaitWaitHandle: waitHandle: WaitHandle * ?millisecondsTimeout: int -> Async<bool> static member CancelDefaultToken: unit -> unit static member Catch: computation: Async<'T> -> Async<Choice<'T,exn>> static member Choice: computations: Async<'T option> seq -> Async<'T option> static member FromBeginEnd: beginAction: (AsyncCallback * objnull -> IAsyncResult) * endAction: (IAsyncResult -> 'T) * ?cancelAction: (unit -> unit) -> Async<'T> + 3 overloads static member FromContinuations: callback: (('T -> unit) * (exn -> unit) * (OperationCanceledException -> unit) -> unit) -> Async<'T> ...
--------------------
type Async<'T>
static member Async.Sleep: millisecondsDueTime: int -> Async<unit>
<summary> Same as AsyncSeq.mapAsync, but the specified function is synchronous. </summary>
<summary> Builds a new asynchronous sequence whose elements are generated by applying the specified function to all elements of the input sequence. The specified function is asynchronous (and the input sequence will be asked for the next element after the processing of an element completes). </summary>
<summary> Same as AsyncSeq.filterAsync, but the specified predicate is synchronous and processes the input element immediately. </summary>
System.String.Contains(value: char) : bool
System.String.Contains(value: string, comparisonType: System.StringComparison) : bool
System.String.Contains(value: char, comparisonType: System.StringComparison) : bool
<summary> Builds a new asynchronous sequence whose elements are those from the input sequence for which the specified function returned true. The specified function is asynchronous (and the input sequence will be asked for the next element after the processing of an element completes). </summary>
FSharp.Control.AsyncSeq