From 158574c021dee7d0d01113d0e0430417f30bcff6 Mon Sep 17 00:00:00 2001 From: Abel Braaksma Date: Sun, 27 Nov 2022 20:51:37 +0100 Subject: [PATCH 1/5] Add signature files for `taskSeq` and builders, plus utils, hide debug stuff etc --- src/FSharp.Control.TaskSeq/AssemblyInfo.fs | 8 + src/FSharp.Control.TaskSeq/DebugUtils.fs | 55 ++ .../FSharp.Control.TaskSeq.fsproj | 13 + src/FSharp.Control.TaskSeq/TaskSeq.fsi | 888 +++++++----------- src/FSharp.Control.TaskSeq/TaskSeqBuilder.fs | 7 +- src/FSharp.Control.TaskSeq/TaskSeqBuilder.fsi | 166 ++++ src/FSharp.Control.TaskSeq/TaskSeqInternal.fs | 17 +- src/FSharp.Control.TaskSeq/Utils.fs | 49 - src/FSharp.Control.TaskSeq/Utils.fsi | 87 ++ 9 files changed, 661 insertions(+), 629 deletions(-) create mode 100644 src/FSharp.Control.TaskSeq/AssemblyInfo.fs create mode 100644 src/FSharp.Control.TaskSeq/DebugUtils.fs create mode 100644 src/FSharp.Control.TaskSeq/TaskSeqBuilder.fsi create mode 100644 src/FSharp.Control.TaskSeq/Utils.fsi diff --git a/src/FSharp.Control.TaskSeq/AssemblyInfo.fs b/src/FSharp.Control.TaskSeq/AssemblyInfo.fs new file mode 100644 index 00000000..d8bda820 --- /dev/null +++ b/src/FSharp.Control.TaskSeq/AssemblyInfo.fs @@ -0,0 +1,8 @@ +namespace TaskSeq.Tests + +open System.Runtime.CompilerServices + +// ensure the test project has access to the internal types +[] + +do () diff --git a/src/FSharp.Control.TaskSeq/DebugUtils.fs b/src/FSharp.Control.TaskSeq/DebugUtils.fs new file mode 100644 index 00000000..aab74e3a --- /dev/null +++ b/src/FSharp.Control.TaskSeq/DebugUtils.fs @@ -0,0 +1,55 @@ +namespace FSharp.Control + +open System.Threading.Tasks +open System +open System.Diagnostics +open System.Threading + +type Debug = + + [] + static val mutable private verbose: bool option + + /// Setting from environment variable TASKSEQ_LOG_VERBOSE, which, + /// when set, enables (very) verbose printing of flow and state + static member private getVerboseSetting() = + match Debug.verbose with + | None -> + let verboseEnv = + try + match Environment.GetEnvironmentVariable "TASKSEQ_LOG_VERBOSE" with + | null -> false + | x -> + match x.ToLowerInvariant().Trim() with + | "1" + | "true" + | "on" + | "yes" -> true + | _ -> false + + with _ -> + false + + Debug.verbose <- Some verboseEnv + verboseEnv + + | Some setting -> setting + + /// Private helper to log to stdout in DEBUG builds only + [] + static member private print value = + match Debug.getVerboseSetting () with + | false -> () + | true -> + // don't use ksprintf here, because the compiler does not remove all allocations due to + // the way PrintfFormat types are compiled, even if we set the Conditional attribute. + let ct = Thread.CurrentThread + printfn "%i (%b): %s" ct.ManagedThreadId ct.IsThreadPoolThread value + + /// Log to stdout in DEBUG builds only + [] + static member logInfo(str) = Debug.print str + + /// Log to stdout in DEBUG builds only + [] + static member logInfo(str, data) = Debug.print $"%s{str}{data}" diff --git a/src/FSharp.Control.TaskSeq/FSharp.Control.TaskSeq.fsproj b/src/FSharp.Control.TaskSeq/FSharp.Control.TaskSeq.fsproj index 2564a319..7ec1202e 100644 --- a/src/FSharp.Control.TaskSeq/FSharp.Control.TaskSeq.fsproj +++ b/src/FSharp.Control.TaskSeq/FSharp.Control.TaskSeq.fsproj @@ -26,6 +26,15 @@ Generates optimized IL code through the new resumable state machines, and comes snupkg + + + + + + + True + + @@ -33,7 +42,11 @@ Generates optimized IL code through the new resumable state machines, and comes \ + + + + diff --git a/src/FSharp.Control.TaskSeq/TaskSeq.fsi b/src/FSharp.Control.TaskSeq/TaskSeq.fsi index 1f0f1497..47f1b1e4 100644 --- a/src/FSharp.Control.TaskSeq/TaskSeq.fsi +++ b/src/FSharp.Control.TaskSeq/TaskSeq.fsi @@ -1,570 +1,322 @@ namespace FSharp.Control + + module TaskSeq = + + val empty<'T> : System.Collections.Generic.IAsyncEnumerable<'T> + + val singleton: + source: 'T -> System.Collections.Generic.IAsyncEnumerable<'T> + + val isEmpty: source: taskSeq<'T> -> System.Threading.Tasks.Task + + val toList: source: taskSeq<'T> -> 'T list + + val toArray: source: taskSeq<'T> -> 'T[] + + val toSeq: source: taskSeq<'T> -> seq<'T> + + val toArrayAsync: + source: taskSeq<'T> -> System.Threading.Tasks.Task<'T[]> + + val toListAsync: + source: taskSeq<'T> -> System.Threading.Tasks.Task<'T list> + + val toResizeArrayAsync: + source: taskSeq<'T> -> System.Threading.Tasks.Task> + + val toIListAsync: + source: taskSeq<'T> -> + System.Threading.Tasks.Task> + + val ofArray: + source: 'T[] -> System.Collections.Generic.IAsyncEnumerable<'T> + + val ofList: + source: 'T list -> System.Collections.Generic.IAsyncEnumerable<'T> + + val ofSeq: + source: seq<'T> -> System.Collections.Generic.IAsyncEnumerable<'T> + + val ofResizeArray: + source: ResizeArray<'T> -> + System.Collections.Generic.IAsyncEnumerable<'T> + + val ofTaskSeq: + source: seq<#System.Threading.Tasks.Task<'T>> -> + System.Collections.Generic.IAsyncEnumerable<'T> + + val ofTaskList: + source: #System.Threading.Tasks.Task<'T> list -> + System.Collections.Generic.IAsyncEnumerable<'T> + + val ofTaskArray: + source: #System.Threading.Tasks.Task<'T> array -> + System.Collections.Generic.IAsyncEnumerable<'T> + + val ofAsyncSeq: + source: seq> -> + System.Collections.Generic.IAsyncEnumerable<'T> + + val ofAsyncList: + source: Async<'T> list -> + System.Collections.Generic.IAsyncEnumerable<'T> + + val ofAsyncArray: + source: Async<'T> array -> + System.Collections.Generic.IAsyncEnumerable<'T> + + val length: source: taskSeq<'T> -> System.Threading.Tasks.Task + + val lengthOrMax: + max: int -> source: taskSeq<'T> -> System.Threading.Tasks.Task + + val lengthBy: + predicate: ('T -> bool) -> + source: taskSeq<'T> -> System.Threading.Tasks.Task + + val lengthByAsync: + predicate: ('T -> #System.Threading.Tasks.Task) -> + source: taskSeq<'T> -> System.Threading.Tasks.Task + + val init: + count: int -> + initializer: (int -> 'T) -> + System.Collections.Generic.IAsyncEnumerable<'T> + + val initInfinite: + initializer: (int -> 'T) -> + System.Collections.Generic.IAsyncEnumerable<'T> + + val initAsync: + count: int -> + initializer: (int -> #System.Threading.Tasks.Task<'T>) -> + System.Collections.Generic.IAsyncEnumerable<'T> + + val initInfiniteAsync: + initializer: (int -> #System.Threading.Tasks.Task<'T>) -> + System.Collections.Generic.IAsyncEnumerable<'T> + + val delay: + generator: (unit -> taskSeq<'T>) -> + System.Collections.Generic.IAsyncEnumerable<'T> + + val concat: + sources: taskSeq<#taskSeq<'T>> -> + System.Collections.Generic.IAsyncEnumerable<'T> + + val append: + source1: #taskSeq<'T> -> + source2: #taskSeq<'T> -> + System.Collections.Generic.IAsyncEnumerable<'T> + + val appendSeq: + source1: #taskSeq<'T> -> + source2: #seq<'T> -> System.Collections.Generic.IAsyncEnumerable<'T> + + val prependSeq: + source1: #seq<'T> -> + source2: #taskSeq<'T> -> + System.Collections.Generic.IAsyncEnumerable<'T> + + val cast: source: taskSeq -> taskSeq<'T> + + val box: + source: taskSeq<'T> -> + System.Collections.Generic.IAsyncEnumerable + + val unbox: source: taskSeq -> taskSeq<'U> when 'U: struct + + val iter: + action: ('T -> unit) -> + source: taskSeq<'T> -> System.Threading.Tasks.Task + + val iteri: + action: (int -> 'T -> unit) -> + source: taskSeq<'T> -> System.Threading.Tasks.Task + + val iterAsync: + action: ('T -> #System.Threading.Tasks.Task) -> + source: taskSeq<'T> -> System.Threading.Tasks.Task + + val iteriAsync: + action: (int -> 'T -> #System.Threading.Tasks.Task) -> + source: taskSeq<'T> -> System.Threading.Tasks.Task + + val map: + mapper: ('T -> 'U) -> + source: taskSeq<'T> -> + System.Collections.Generic.IAsyncEnumerable<'U> + + val mapi: + mapper: (int -> 'T -> 'U) -> + source: taskSeq<'T> -> + System.Collections.Generic.IAsyncEnumerable<'U> + + val mapAsync: + mapper: ('T -> #System.Threading.Tasks.Task<'U>) -> + source: taskSeq<'T> -> + System.Collections.Generic.IAsyncEnumerable<'U> + + val mapiAsync: + mapper: (int -> 'T -> #System.Threading.Tasks.Task<'U>) -> + source: taskSeq<'T> -> + System.Collections.Generic.IAsyncEnumerable<'U> + + val collect: + binder: ('T -> #System.Collections.Generic.IAsyncEnumerable<'U>) -> + source: taskSeq<'T> -> + System.Collections.Generic.IAsyncEnumerable<'U> + + val collectSeq: + binder: ('T -> #seq<'U>) -> + source: taskSeq<'T> -> + System.Collections.Generic.IAsyncEnumerable<'U> + + val collectAsync: + binder: ('T -> #System.Threading.Tasks.Task<'TSeqU>) -> + source: taskSeq<'T> -> taskSeq<'U> + when 'TSeqU :> System.Collections.Generic.IAsyncEnumerable<'U> + + val collectSeqAsync: + binder: ('T -> #System.Threading.Tasks.Task<'SeqU>) -> + source: taskSeq<'T> -> taskSeq<'U> when 'SeqU :> seq<'U> + + val tryHead: + source: taskSeq<'T> -> System.Threading.Tasks.Task<'T option> + + val head: source: taskSeq<'T> -> System.Threading.Tasks.Task<'T> + + val tryLast: + source: taskSeq<'T> -> System.Threading.Tasks.Task<'T option> + + val last: source: taskSeq<'T> -> System.Threading.Tasks.Task<'T> + + val tryTail: + source: taskSeq<'T> -> + System.Threading.Tasks.Task option> + + val tail: + source: taskSeq<'T> -> + System.Threading.Tasks.Task> + + val tryItem: + index: int -> + source: taskSeq<'T> -> System.Threading.Tasks.Task<'T option> + + val item: + index: int -> source: taskSeq<'T> -> System.Threading.Tasks.Task<'T> + + val tryExactlyOne: + source: taskSeq<'T> -> System.Threading.Tasks.Task<'T option> + + val exactlyOne: source: taskSeq<'T> -> System.Threading.Tasks.Task<'T> + + val indexed: + source: taskSeq<'T> -> + System.Collections.Generic.IAsyncEnumerable + + val choose: + chooser: ('T -> 'U option) -> + source: taskSeq<'T> -> + System.Collections.Generic.IAsyncEnumerable<'U> + + val chooseAsync: + chooser: ('T -> #System.Threading.Tasks.Task<'U option>) -> + source: taskSeq<'T> -> + System.Collections.Generic.IAsyncEnumerable<'U> + + val filter: + predicate: ('T -> bool) -> + source: taskSeq<'T> -> + System.Collections.Generic.IAsyncEnumerable<'T> + + val filterAsync: + predicate: ('T -> #System.Threading.Tasks.Task) -> + source: taskSeq<'T> -> + System.Collections.Generic.IAsyncEnumerable<'T> + + val tryPick: + chooser: ('T -> 'U option) -> + source: taskSeq<'T> -> System.Threading.Tasks.Task<'U option> + + val tryPickAsync: + chooser: ('T -> #System.Threading.Tasks.Task<'U option>) -> + source: taskSeq<'T> -> System.Threading.Tasks.Task<'U option> + + val tryFind: + predicate: ('T -> bool) -> + source: taskSeq<'T> -> System.Threading.Tasks.Task<'T option> + + val tryFindAsync: + predicate: ('T -> #System.Threading.Tasks.Task) -> + source: taskSeq<'T> -> System.Threading.Tasks.Task<'T option> + + val tryFindIndex: + predicate: ('T -> bool) -> + source: taskSeq<'T> -> System.Threading.Tasks.Task + + val tryFindIndexAsync: + predicate: ('T -> #System.Threading.Tasks.Task) -> + source: taskSeq<'T> -> System.Threading.Tasks.Task + + val except: + itemsToExclude: taskSeq<'T> -> + source: taskSeq<'T> -> + System.Collections.Generic.IAsyncEnumerable<'T> when 'T: equality + + val exceptOfSeq: + itemsToExclude: seq<'T> -> + source: taskSeq<'T> -> + System.Collections.Generic.IAsyncEnumerable<'T> when 'T: equality + + val exists: + predicate: ('T -> bool) -> + source: taskSeq<'T> -> System.Threading.Tasks.Task + + val existsAsync: + predicate: ('T -> #System.Threading.Tasks.Task) -> + source: taskSeq<'T> -> System.Threading.Tasks.Task + + val contains: + value: 'T -> source: taskSeq<'T> -> System.Threading.Tasks.Task + when 'T: equality + + val pick: + chooser: ('T -> 'U option) -> + source: taskSeq<'T> -> System.Threading.Tasks.Task<'U> + + val pickAsync: + chooser: ('T -> #System.Threading.Tasks.Task<'U option>) -> + source: taskSeq<'T> -> System.Threading.Tasks.Task<'U> + + val find: + predicate: ('T -> bool) -> + source: taskSeq<'T> -> System.Threading.Tasks.Task<'T> + + val findAsync: + predicate: ('T -> #System.Threading.Tasks.Task) -> + source: taskSeq<'T> -> System.Threading.Tasks.Task<'T> + + val findIndex: + predicate: ('T -> bool) -> + source: taskSeq<'T> -> System.Threading.Tasks.Task + + val findIndexAsync: + predicate: ('T -> #System.Threading.Tasks.Task) -> + source: taskSeq<'T> -> System.Threading.Tasks.Task + + val zip: + source1: taskSeq<'T> -> + source2: taskSeq<'U> -> + System.Collections.Generic.IAsyncEnumerable<'T * 'U> + + val fold: + folder: ('State -> 'T -> 'State) -> + state: 'State -> + source: taskSeq<'T> -> System.Threading.Tasks.Task<'State> + + val foldAsync: + folder: ('State -> 'T -> #System.Threading.Tasks.Task<'State>) -> + state: 'State -> + source: taskSeq<'T> -> System.Threading.Tasks.Task<'State> -#nowarn "1204" - -module TaskSeq = - open System.Collections.Generic - open System.Threading.Tasks - - /// Initialize an empty taskSeq. - val empty<'T> : taskSeq<'T> - - /// - /// Creates a sequence from that generates a single element and then ends. - /// - val singleton: source: 'T -> taskSeq<'T> - - /// - /// Returns if the task sequence contains no elements, otherwise. - /// - val isEmpty: source: taskSeq<'T> -> Task - - /// - /// Returns the length of the sequence. This operation requires the whole sequence to be evaluated and - /// should not be used on potentially infinite sequences, see for an alternative. - /// - val length: source: taskSeq<'T> -> Task - - /// - /// Returns the length of the sequence, or , whichever comes first. This operation requires the task sequence - /// to be evaluated in full, or until items have been processed. Use this method instead of - /// if you want to prevent too many items to be evaluated, or if the sequence is potentially infinite. - /// - val lengthOrMax: max: int -> source: taskSeq<'T> -> Task - - /// - /// Returns the length of the sequence of all items for which the returns true. - /// This operation requires the whole sequence to be evaluated and should not be used on potentially infinite sequences. - /// - val lengthBy: predicate: ('T -> bool) -> source: taskSeq<'T> -> Task - - /// - /// Returns the length of the sequence of all items for which the returns true. - /// This operation requires the whole sequence to be evaluated and should not be used on potentially infinite sequences. - /// If does not need to be asynchronous, consider using . - /// - val lengthByAsync: predicate: ('T -> #Task) -> source: taskSeq<'T> -> Task - - /// - /// Returns a task sequence that is given by the delayed specification of a task sequence. - /// - /// - /// The generating function for the task sequence. - /// The generated task sequence. - val delay: generator: (unit -> taskSeq<'T>) -> taskSeq<'T> - - /// - /// Generates a new task sequence which, when iterated, will return successive elements by calling the given function - /// with the current index, up to the given count. Each element is saved after its initialization for successive access to - /// , which will not re-evaluate the . However, - /// re-iterating the returned task sequence will re-evaluate the initialization function. The returned sequence may - /// be passed between threads safely. However, individual IEnumerator values generated from the returned sequence should - /// not be accessed concurrently. - /// - /// - /// The maximum number of items to generate for the sequence. - /// A function that generates an item in the sequence from a given index. - /// The resulting task sequence. - /// Thrown when count is negative. - val init: count: int -> initializer: (int -> 'T) -> taskSeq<'T> - - /// - /// Generates a new task sequence which, when iterated, will return successive elements by calling the given function - /// with the current index, up to the given count. Each element is saved after its initialization for successive access to - /// , which will not re-evaluate the . However, - /// re-iterating the returned task sequence will re-evaluate the initialization function. The returned sequence may - /// be passed between threads safely. However, individual IEnumerator values generated from the returned sequence should - /// not be accessed concurrently. - /// - /// - /// The maximum number of items to generate for the sequence. - /// A function that generates an item in the sequence from a given index. - /// The resulting task sequence. - /// Thrown when count is negative. - val initAsync: count: int -> initializer: (int -> #Task<'T>) -> taskSeq<'T> - - /// - /// Generates a new task sequence which, when iterated, will return successive elements by calling the given function - /// with the current index, ad infinitum, or until is reached. - /// Each element is saved after its initialization for successive access to - /// , which will not re-evaluate the . However, - /// re-iterating the returned task sequence will re-evaluate the initialization function. The returned sequence may - /// be passed between threads safely. However, individual IEnumerator values generated from the returned sequence should - /// not be accessed concurrently. - /// - /// - /// A function that generates an item in the sequence from a given index. - /// The resulting task sequence. - val initInfinite: initializer: (int -> 'T) -> taskSeq<'T> - - /// - /// Generates a new task sequence which, when iterated, will return successive elements by calling the given function - /// with the current index, ad infinitum, or until is reached. - /// Each element is saved after its initialization for successive access to - /// , which will not re-evaluate the . However, - /// re-iterating the returned task sequence will re-evaluate the initialization function. The returned sequence may - /// be passed between threads safely. However, individual IEnumerator values generated from the returned sequence should - /// not be accessed concurrently. - /// - /// - /// A function that generates an item in the sequence from a given index. - /// The resulting task sequence. - val initInfiniteAsync: initializer: (int -> #Task<'T>) -> taskSeq<'T> - - /// - /// Combines the given task sequence of task sequences and concatenates them end-to-end, to form a - /// new flattened, single task sequence. Each task sequence is awaited item by item, before the next is iterated. - /// - /// - /// The input enumeration-of-enumerations. - /// The resulting task sequence. - /// Thrown when the input sequence is null. - val concat: sources: taskSeq<#taskSeq<'T>> -> taskSeq<'T> - - /// - /// Concatenates task sequences and in order as a single - /// task sequence. - /// - /// - /// The first input task sequence. - /// The second input task sequence. - /// The resulting task sequence. - /// Thrown when either of the input sequences is null. - val append: source1: #taskSeq<'T> -> source2: #taskSeq<'T> -> taskSeq<'T> - - /// - /// Concatenates a task sequence with a non-async F# in - /// and returns a single task sequence. - /// - /// - /// The input task sequence. - /// The input F# sequence. - /// The resulting task sequence. - /// Thrown when either of the input sequences is null. - val appendSeq: source1: #taskSeq<'T> -> source2: #seq<'T> -> taskSeq<'T> - - /// - /// Concatenates a non-async F# in with a task sequence in - /// and returns a single task sequence. - /// - /// - /// The input F# sequence. - /// The input task sequence. - /// The resulting task sequence. - /// Thrown when either of the input sequences is null. - val prependSeq: source1: #seq<'T> -> source2: #taskSeq<'T> -> taskSeq<'T> - - /// Returns taskSeq as an array. This function is blocking until the sequence is exhausted and will properly dispose of the resources. - val toList: source: taskSeq<'T> -> 'T list - - /// Returns taskSeq as an array. This function is blocking until the sequence is exhausted and will properly dispose of the resources. - val toArray: source: taskSeq<'T> -> 'T[] - - /// - /// Returns the task sequence as an F# , that is, an - /// . This function is blocking at each , but otherwise - /// acts as a normal delay-executed sequence. - /// It will then dispose of the resources. - /// - /// - /// The input task sequence. - /// The resulting task sequence. - val toSeq: source: taskSeq<'T> -> seq<'T> - - /// Unwraps the taskSeq as a Task>. This function is non-blocking. - val toArrayAsync: source: taskSeq<'T> -> Task<'T[]> - - /// Unwraps the taskSeq as a Task>. This function is non-blocking. - val toListAsync: source: taskSeq<'T> -> Task<'T list> - - /// Unwraps the taskSeq as a Task>. This function is non-blocking. - val toResizeArrayAsync: source: taskSeq<'T> -> Task> - - /// Unwraps the taskSeq as a Task>. This function is non-blocking. - val toIListAsync: source: taskSeq<'T> -> Task> - - /// Create a taskSeq of an array. - val ofArray: source: 'T[] -> taskSeq<'T> - - /// Create a taskSeq of a list. - val ofList: source: 'T list -> taskSeq<'T> - - /// Create a taskSeq of a seq. - val ofSeq: source: seq<'T> -> taskSeq<'T> - - /// Create a taskSeq of a ResizeArray, aka List. - val ofResizeArray: source: ResizeArray<'T> -> taskSeq<'T> - - /// Create a taskSeq of a sequence of tasks, that may already have hot-started. - val ofTaskSeq: source: seq<#Task<'T>> -> taskSeq<'T> - - /// Create a taskSeq of a list of tasks, that may already have hot-started. - val ofTaskList: source: #Task<'T> list -> taskSeq<'T> - - /// Create a taskSeq of an array of tasks, that may already have hot-started. - val ofTaskArray: source: #Task<'T> array -> taskSeq<'T> - - /// Create a taskSeq of a seq of async. - val ofAsyncSeq: source: seq> -> taskSeq<'T> - - /// Create a taskSeq of a list of async. - val ofAsyncList: source: Async<'T> list -> taskSeq<'T> - - /// Create a taskSeq of an array of async. - val ofAsyncArray: source: Async<'T> array -> taskSeq<'T> - - /// - /// Boxes as type each item in the sequence asynchyronously. - /// - val box: source: taskSeq<'T> -> taskSeq - - /// - /// Unboxes to the target type each item in the sequence asynchyronously. - /// The target type must be a or a built-in value type. - /// - /// Thrown when the function is unable to cast an item to the target type. - val unbox<'U when 'U: struct> : source: taskSeq -> taskSeq<'U> - - /// - /// Casts each item in the untyped sequence asynchyronously. If your types are boxed struct types - /// it is recommended to use instead. - /// - /// Thrown when the function is unable to cast an item to the target type. - val cast: source: taskSeq -> taskSeq<'T> - - /// Iterates over the taskSeq applying the action function to each item. This function is non-blocking - /// exhausts the sequence as soon as the task is evaluated. - val iter: action: ('T -> unit) -> source: taskSeq<'T> -> Task - - /// Iterates over the taskSeq applying the action function to each item. This function is non-blocking, - /// exhausts the sequence as soon as the task is evaluated. - val iteri: action: (int -> 'T -> unit) -> source: taskSeq<'T> -> Task - - /// Iterates over the taskSeq applying the async action to each item. This function is non-blocking - /// exhausts the sequence as soon as the task is evaluated. - val iterAsync: action: ('T -> #Task) -> source: taskSeq<'T> -> Task - - /// Iterates over the taskSeq, applying the async action to each item. This function is non-blocking, - /// exhausts the sequence as soon as the task is evaluated. - val iteriAsync: action: (int -> 'T -> #Task) -> source: taskSeq<'T> -> Task - - /// Maps over the taskSeq, applying the mapper function to each item. This function is non-blocking. - val map: mapper: ('T -> 'U) -> source: taskSeq<'T> -> taskSeq<'U> - - /// - /// Builds a new task sequence whose elements are the corresponding elements of the input task - /// sequence paired with the integer index (from 0) of each element. - /// Does not evaluate the input sequence until requested. - /// - /// The input task sequence. - /// The resulting task sequence of tuples. - /// Thrown when the input sequence is null. - val indexed: source: taskSeq<'T> -> taskSeq - - /// Maps over the taskSeq with an index, applying the mapper function to each item. This function is non-blocking. - val mapi: mapper: (int -> 'T -> 'U) -> source: taskSeq<'T> -> taskSeq<'U> - - /// Maps over the taskSeq, applying the async mapper function to each item. This function is non-blocking. - val mapAsync: mapper: ('T -> #Task<'U>) -> source: taskSeq<'T> -> taskSeq<'U> - - /// Maps over the taskSeq with an index, applying the async mapper function to each item. This function is non-blocking. - val mapiAsync: mapper: (int -> 'T -> #Task<'U>) -> source: taskSeq<'T> -> taskSeq<'U> - - /// Applies the given function to the items in the taskSeq and concatenates all the results in order. - val collect: binder: ('T -> #taskSeq<'U>) -> source: taskSeq<'T> -> taskSeq<'U> - - /// Applies the given function to the items in the taskSeq and concatenates all the results in order. - val collectSeq: binder: ('T -> #seq<'U>) -> source: taskSeq<'T> -> taskSeq<'U> - - /// Applies the given async function to the items in the taskSeq and concatenates all the results in order. - val collectAsync: binder: ('T -> #Task<'TSeqU>) -> source: taskSeq<'T> -> taskSeq<'U> when 'TSeqU :> taskSeq<'U> - - /// Applies the given async function to the items in the taskSeq and concatenates all the results in order. - val collectSeqAsync: binder: ('T -> #Task<'SeqU>) -> source: taskSeq<'T> -> taskSeq<'U> when 'SeqU :> seq<'U> - - /// - /// Returns the first element of the task sequence from , or if the sequence is empty. - /// - val tryHead: source: taskSeq<'T> -> Task<'T option> - - /// - /// Returns the first elementof the task sequence from - /// - /// Thrown when the sequence is empty. - val head: source: taskSeq<'T> -> Task<'T> - - /// - /// Returns the whole task sequence from , minus its first element, or if the sequence is empty. - /// - val tryTail: source: taskSeq<'T> -> Task option> - - /// - /// Returns the whole task sequence from , minus its first element. - /// - /// Thrown when the sequence is empty. - val tail: source: taskSeq<'T> -> Task> - - /// - /// Returns the last element of the task sequence from , or if the sequence is empty. - /// - val tryLast: source: taskSeq<'T> -> Task<'T option> - - /// - /// Returns the last element of the . - /// - /// Thrown when the sequence is empty. - val last: source: taskSeq<'T> -> Task<'T> - - /// - /// Returns the nth element of the , or if the sequence - /// does not contain enough elements, or if is negative. - /// Parameter is zero-based, that is, the value 0 returns the first element. - /// - val tryItem: index: int -> source: taskSeq<'T> -> Task<'T option> - - /// - /// Returns the nth element of the , or if the sequence - /// does not contain enough elements, or if is negative. - /// - /// Thrown when the sequence has insufficient length or - /// is negative. - val item: index: int -> source: taskSeq<'T> -> Task<'T> - - /// - /// Returns the only element of the task sequence, or if the sequence is empty of - /// contains more than one element. - /// - val tryExactlyOne: source: taskSeq<'T> -> Task<'T option> - - /// - /// Returns the only element of the task sequence. - /// - /// Thrown when the input sequence does not contain precisely one element. - val exactlyOne: source: taskSeq<'T> -> Task<'T> - - /// - /// Applies the given function to each element of the task sequence. Returns - /// a sequence comprised of the results "x" for each element where - /// the function returns Some(x). - /// If is asynchronous, consider using . - /// - val choose: chooser: ('T -> 'U option) -> source: taskSeq<'T> -> taskSeq<'U> - - /// - /// Applies the given asynchronous function to each element of the task sequence. Returns - /// a sequence comprised of the results "x" for each element where - /// the function returns . - /// If does not need to be asynchronous, consider using . - /// - val chooseAsync: chooser: ('T -> #Task<'U option>) -> source: taskSeq<'T> -> taskSeq<'U> - - /// - /// Returns a new collection containing only the elements of the collection - /// for which the given function returns . - /// If is asynchronous, consider using . - /// - val filter: predicate: ('T -> bool) -> source: taskSeq<'T> -> taskSeq<'T> - - /// - /// Returns a new collection containing only the elements of the collection - /// for which the given asynchronous function returns . - /// If does not need to be asynchronous, consider using . - /// - val filterAsync: predicate: ('T -> #Task) -> source: taskSeq<'T> -> taskSeq<'T> - - /// - /// Applies the given function to successive elements of the task sequence - /// in , returning the first result where the function returns . - /// If is asynchronous, consider using . - /// - val tryPick: chooser: ('T -> 'U option) -> source: taskSeq<'T> -> Task<'U option> - - /// - /// Applies the given asynchronous function to successive elements of the task sequence - /// in , returning the first result where the function returns . - /// If does not need to be asynchronous, consider using . - /// - val tryPickAsync: chooser: ('T -> #Task<'U option>) -> source: taskSeq<'T> -> Task<'U option> - - /// - /// Returns the first element of the task sequence in for which the given function - /// returns . Returns if no such element exists. - /// If is asynchronous, consider using . - /// - val tryFind: predicate: ('T -> bool) -> source: taskSeq<'T> -> Task<'T option> - - /// - /// Returns the first element of the task sequence in for which the given asynchronous function - /// returns . Returns if no such element exists. - /// If does not need to be asynchronous, consider using . - /// - val tryFindAsync: predicate: ('T -> #Task) -> source: taskSeq<'T> -> Task<'T option> - - /// - /// Returns the index, starting from zero, of the task sequence in for which the given function - /// returns . Returns if no such element exists. - /// If is asynchronous, consider using . - /// - val tryFindIndex: predicate: ('T -> bool) -> source: taskSeq<'T> -> Task - - /// - /// Returns the index, starting from zero, of the task sequence in for which the given asynchronous function - /// returns . Returns if no such element exists. - /// If does not need to be asynchronous, consider using . - /// - val tryFindIndexAsync: predicate: ('T -> #Task) -> source: taskSeq<'T> -> Task - - - /// - /// Applies the given function to successive elements of the task sequence - /// in , returning the first result where the function returns . - /// If is asynchronous, consider using . - /// Thrown when every item of the sequence - /// evaluates to when the given function is applied. - /// - val pick: chooser: ('T -> 'U option) -> source: taskSeq<'T> -> Task<'U> - - /// - /// Applies the given asynchronous function to successive elements of the task sequence - /// in , returning the first result where the function returns . - /// If does not need to be asynchronous, consider using . - /// Thrown when every item of the sequence - /// evaluates to when the given function is applied. - /// - val pickAsync: chooser: ('T -> #Task<'U option>) -> source: taskSeq<'T> -> Task<'U> - - /// - /// Returns the first element of the task sequence in for which the given function - /// returns . - /// If is asynchronous, consider using . - /// - /// Thrown if no element returns when - /// evaluated by the function. - val find: predicate: ('T -> bool) -> source: taskSeq<'T> -> Task<'T> - - /// - /// Returns the first element of the task sequence in for which the given - /// asynchronous function returns . - /// If does not need to be asynchronous, consider using . - /// - /// Thrown if no element returns when - /// evaluated by the function. - val findAsync: predicate: ('T -> #Task) -> source: taskSeq<'T> -> Task<'T> - - /// - /// Returns the index, starting from zero, of the first element of the task sequence in for which - /// the given function returns . - /// If is asynchronous, consider using . - /// - /// Thrown if no element returns when - /// evaluated by the function. - val findIndex: predicate: ('T -> bool) -> source: taskSeq<'T> -> Task - - /// - /// Returns the index, starting from zero, of the task sequence in for which the given - /// asynchronous function returns . - /// If does not need to be asynchronous, consider using . - /// - /// - /// Thrown if no element returns when - /// evaluated by the function. - val findIndexAsync: predicate: ('T -> #Task) -> source: taskSeq<'T> -> Task - - /// - /// Tests if the sequence contains the specified element. Returns - /// if contains the specified element; - /// otherwise. - /// - /// - /// The value to locate in the input sequence. - /// The input sequence. - /// True if the input sequence contains the specified element; false otherwise. - /// Thrown when the input sequence is null. - val contains<'T when 'T: equality> : value: 'T -> source: taskSeq<'T> -> Task - - /// - /// Tests if any element of the task sequence in satisfies - /// the given . - /// The function is applied to the elements of the input sequence. If any application - /// returns then the overall result is and no further elements are evaluated and tested. - /// Otherwise, is returned. - /// - /// - /// A function to test each item of the input sequence. - /// The input sequence. /// - /// True if any result from the predicate is true; false otherwise. /// - /// Thrown when the input sequence is null. - val exists: predicate: ('T -> bool) -> source: taskSeq<'T> -> Task - - /// - /// Tests if any element of the task sequence in satisfies - /// the given async . - /// The function is applied to the elements of the input sequence. If any application - /// returns then the overall result is and no further elements are evaluated and tested. - /// Otherwise, is returned. - /// - /// - /// A function to test each item of the input sequence. - /// The input sequence. /// - /// True if any result from the predicate is true; false otherwise. /// - /// Thrown when the input sequence is null. - val existsAsync: predicate: ('T -> #Task) -> source: taskSeq<'T> -> Task - - /// - /// Returns a new task sequence with the distinct elements of the second task sequence which do not appear in the - /// , using generic hash and equality comparisons to compare values. - /// - /// - /// - /// Note that this function returns a task sequence that digests the whole of the first input task sequence as soon as - /// the result sequence first gets awaited or iterated. As a result this function should not be used with - /// large or infinite sequences in the first parameter. The function makes no assumption on the ordering of the first input - /// sequence. - /// - /// - /// A task sequence whose elements that also occur in the second sequence will cause those elements to be removed from the returned sequence. - /// A sequence whose elements that are not also in first will be returned. - /// A sequence that contains the set difference of the elements of two sequences. - /// - /// Thrown when either of the two input sequences is null. - val except<'T when 'T: equality> : itemsToExclude: taskSeq<'T> -> source: taskSeq<'T> -> taskSeq<'T> - - /// - /// Returns a new task sequence with the distinct elements of the second task sequence which do not appear in the - /// , using generic hash and equality comparisons to compare values. - /// - /// - /// - /// Note that this function returns a task sequence that digests the whole of the first input task sequence as soon as - /// the result sequence first gets awaited or iterated. As a result this function should not be used with - /// large or infinite sequences in the first parameter. The function makes no assumption on the ordering of the first input - /// sequence. - /// - /// - /// A task sequence whose elements that also occur in the second sequence will cause those elements to be removed from the returned sequence. - /// A sequence whose elements that are not also in first will be returned. - /// A sequence that contains the set difference of the elements of two sequences. - /// - /// Thrown when either of the two input sequences is null. - val exceptOfSeq<'T when 'T: equality> : itemsToExclude: seq<'T> -> source: taskSeq<'T> -> taskSeq<'T> - - /// - /// Zips two task sequences, returning a taskSeq of the tuples of each sequence, in order. May raise ArgumentException - /// if the sequences are or unequal length. - /// - /// The sequences have different lengths. - val zip: source1: taskSeq<'T> -> source2: taskSeq<'U> -> taskSeq<'T * 'U> - - /// - /// Applies the function to each element in the task sequence, - /// threading an accumulator argument of type through the computation. - /// If the accumulator function is asynchronous, consider using . - /// - val fold: folder: ('State -> 'T -> 'State) -> state: 'State -> source: taskSeq<'T> -> Task<'State> - - /// - /// Applies the asynchronous function to each element in the task sequence, - /// threading an accumulator argument of type through the computation. - /// If the accumulator function does not need to be asynchronous, consider using . - /// - val foldAsync: folder: ('State -> 'T -> #Task<'State>) -> state: 'State -> source: taskSeq<'T> -> Task<'State> diff --git a/src/FSharp.Control.TaskSeq/TaskSeqBuilder.fs b/src/FSharp.Control.TaskSeq/TaskSeqBuilder.fs index ea154804..a9d4d52f 100644 --- a/src/FSharp.Control.TaskSeq/TaskSeqBuilder.fs +++ b/src/FSharp.Control.TaskSeq/TaskSeqBuilder.fs @@ -40,7 +40,7 @@ module Internal = // cannot be marked with 'internal' scope /// Call MoveNext on an IAsyncStateMachine by reference let inline moveNextRef (x: byref<'T> when 'T :> IAsyncStateMachine) = x.MoveNext() - // F# requires that we implement interfaces even on an abstract class + /// F# requires that we implement interfaces even on an abstract class let inline raiseNotImpl () = NotImplementedException "Abstract Class: method or property not implemented" |> raise @@ -660,3 +660,8 @@ module HighPriority = sm.Data.awaiter <- awaiter sm.Data.current <- ValueNone false) + +[] +module TaskSeqBuilder = + /// Builds an asynchronous task sequence based on IAsyncEnumerable<'T> using computation expression syntax. + let taskSeq = TaskSeqBuilder() diff --git a/src/FSharp.Control.TaskSeq/TaskSeqBuilder.fsi b/src/FSharp.Control.TaskSeq/TaskSeqBuilder.fsi new file mode 100644 index 00000000..bfd082f8 --- /dev/null +++ b/src/FSharp.Control.TaskSeq/TaskSeqBuilder.fsi @@ -0,0 +1,166 @@ +namespace FSharp.Control + +open System +open System.Threading +open System.Threading.Tasks +open System.Threading.Tasks.Sources +open System.Runtime.CompilerServices +open System.Collections.Generic + +open FSharp.Core.CompilerServices + +[] +module Internal = + + /// Setting from environment variable TASKSEQ_LOG_VERBOSE, which, + /// when set, enables (very) verbose printing of flow and state + val initVerbose: unit -> bool + + /// Call MoveNext on an IAsyncStateMachine by reference + val inline moveNextRef: x: byref<#IAsyncStateMachine> -> unit + + val inline raiseNotImpl: unit -> 'a + +type taskSeq<'T> = IAsyncEnumerable<'T> + +[] +type TaskSeqStateMachineData<'T> = + + new: unit -> TaskSeqStateMachineData<'T> + + [] + val mutable cancellationToken: CancellationToken + + /// Keeps track of the objects that need to be disposed off on IAsyncDispose. + [] + val mutable disposalStack: ResizeArray<(unit -> Task)> + + [] + val mutable awaiter: ICriticalNotifyCompletion + + [] + val mutable promiseOfValueOrEnd: ManualResetValueTaskSourceCore + + /// Helper struct providing methods for awaiting 'next' in async iteration scenarios. + [] + val mutable builder: AsyncIteratorMethodBuilder + + /// Whether or not a full iteration through the IAsyncEnumerator has completed + [] + val mutable completed: bool + + /// Used by the AsyncEnumerator interface to return the Current value when + /// IAsyncEnumerator.Current is called + [] + val mutable current: ValueOption<'T> + + /// A reference to 'self', because otherwise we can't use byref in the resumable code. + [] + val mutable boxedSelf: TaskSeq<'T> + + member PopDispose: unit -> unit + + member PushDispose: disposer: (unit -> Task) -> unit + +and [] TaskSeq<'T> = + interface IValueTaskSource + interface IValueTaskSource + interface IAsyncStateMachine + interface IAsyncEnumerable<'T> + interface IAsyncEnumerator<'T> + + new: unit -> TaskSeq<'T> + + abstract MoveNextAsyncResult: unit -> ValueTask + +and [] TaskSeq<'Machine, 'T + when 'Machine :> IAsyncStateMachine and 'Machine :> IResumableStateMachine>> = + inherit TaskSeq<'T> + interface IAsyncEnumerator<'T> + interface IAsyncEnumerable<'T> + interface IAsyncStateMachine + interface IValueTaskSource + interface IValueTaskSource + + new: unit -> TaskSeq<'Machine, 'T> + + [] + val mutable _initialMachine: 'Machine + + /// Keeps the active state machine. + [] + val mutable _machine: 'Machine + + //new: unit -> TaskSeq<'Machine, 'T> + member InitMachineData: ct: CancellationToken * machine: byref<'Machine> -> unit + override MoveNextAsyncResult: unit -> ValueTask + +and TaskSeqCode<'T> = ResumableCode, unit> +and TaskSeqStateMachine<'T> = ResumableStateMachine> +and TaskSeqResumptionFunc<'T> = ResumptionFunc> +and TaskSeqResumptionDynamicInfo<'T> = ResumptionDynamicInfo> + +[] +type TaskSeqBuilder = + + member inline Combine: task1: TaskSeqCode<'T> * task2: TaskSeqCode<'T> -> TaskSeqCode<'T> + member inline Delay: f: (unit -> TaskSeqCode<'T>) -> TaskSeqCode<'T> + member inline Run: code: TaskSeqCode<'T> -> IAsyncEnumerable<'T> + member inline TryFinally: body: TaskSeqCode<'T> * compensation: (unit -> unit) -> TaskSeqCode<'T> + member inline TryFinallyAsync: body: TaskSeqCode<'T> * compensation: (unit -> Task) -> TaskSeqCode<'T> + member inline TryWith: body: TaskSeqCode<'T> * catch: (exn -> TaskSeqCode<'T>) -> TaskSeqCode<'T> + member inline Using: disp: 'a * body: ('a -> TaskSeqCode<'T>) -> TaskSeqCode<'T> when 'a :> IAsyncDisposable + member inline While: condition: (unit -> bool) * body: TaskSeqCode<'T> -> TaskSeqCode<'T> + /// Used by `For`. F# currently doesn't support `while!`, so this cannot be called directly from the CE + member inline WhileAsync: condition: (unit -> ValueTask) * body: TaskSeqCode<'T> -> TaskSeqCode<'T> + member inline Yield: v: 'T -> TaskSeqCode<'T> + member inline Zero: unit -> TaskSeqCode<'T> + +[] +module TaskSeqBuilder = + + /// + /// Builds an asynchronous task sequence based on using computation expression syntax. + /// + val taskSeq: TaskSeqBuilder + +[] +module LowPriority = + type TaskSeqBuilder with + + [] + member inline Bind< ^TaskLike, 'TResult1, 'TResult2, ^Awaiter, 'TOverall> : + task: ^TaskLike * continuation: ('TResult1 -> TaskSeqCode<'TResult2>) -> TaskSeqCode<'TResult2> + when ^TaskLike: (member GetAwaiter: unit -> ^Awaiter) + and ^Awaiter :> ICriticalNotifyCompletion + and ^Awaiter: (member get_IsCompleted: unit -> bool) + and ^Awaiter: (member GetResult: unit -> 'TResult1) + +[] +module MediumPriority = + type TaskSeqBuilder with + + member inline Using: disp: 'a * body: ('a -> TaskSeqCode<'T>) -> TaskSeqCode<'T> when 'a :> IDisposable + + type TaskSeqBuilder with + + member inline For: sequence: seq<'TElement> * body: ('TElement -> TaskSeqCode<'T>) -> TaskSeqCode<'T> + + type TaskSeqBuilder with + + member inline YieldFrom: source: seq<'T> -> TaskSeqCode<'T> + + type TaskSeqBuilder with + + member inline For: + source: #IAsyncEnumerable<'TElement> * body: ('TElement -> TaskSeqCode<'T>) -> TaskSeqCode<'T> + + type TaskSeqBuilder with + + member inline YieldFrom: source: IAsyncEnumerable<'T> -> TaskSeqCode<'T> + +[] +module HighPriority = + type TaskSeqBuilder with + + member inline Bind: task: Task<'TResult1> * continuation: ('TResult1 -> TaskSeqCode<'T>) -> TaskSeqCode<'T> diff --git a/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs b/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs index deeaefbd..8f7446ef 100644 --- a/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs +++ b/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs @@ -5,41 +5,36 @@ open System.Collections.Generic open System.Threading open System.Threading.Tasks -[] -module ExtraTaskSeqOperators = - /// A TaskSeq workflow for IAsyncEnumerable<'T> types. - let taskSeq = TaskSeqBuilder() - [] -type AsyncEnumStatus = +type internal AsyncEnumStatus = | BeforeAll | WithCurrent | AfterAll [] -type Action<'T, 'U, 'TaskU when 'TaskU :> Task<'U>> = +type internal Action<'T, 'U, 'TaskU when 'TaskU :> Task<'U>> = | CountableAction of countable_action: (int -> 'T -> 'U) | SimpleAction of simple_action: ('T -> 'U) | AsyncCountableAction of async_countable_action: (int -> 'T -> 'TaskU) | AsyncSimpleAction of async_simple_action: ('T -> 'TaskU) [] -type FolderAction<'T, 'State, 'TaskState when 'TaskState :> Task<'State>> = +type internal FolderAction<'T, 'State, 'TaskState when 'TaskState :> Task<'State>> = | FolderAction of state_action: ('State -> 'T -> 'State) | AsyncFolderAction of async_state_action: ('State -> 'T -> 'TaskState) [] -type ChooserAction<'T, 'U, 'TaskOption when 'TaskOption :> Task<'U option>> = +type internal ChooserAction<'T, 'U, 'TaskOption when 'TaskOption :> Task<'U option>> = | TryPick of try_pick: ('T -> 'U option) | TryPickAsync of async_try_pick: ('T -> 'TaskOption) [] -type PredicateAction<'T, 'TaskBool when 'TaskBool :> Task> = +type internal PredicateAction<'T, 'TaskBool when 'TaskBool :> Task> = | Predicate of try_filter: ('T -> bool) | PredicateAsync of async_try_filter: ('T -> 'TaskBool) [] -type InitAction<'T, 'TaskT when 'TaskT :> Task<'T>> = +type internal InitAction<'T, 'TaskT when 'TaskT :> Task<'T>> = | InitAction of init_item: (int -> 'T) | InitActionAsync of async_init_item: (int -> 'TaskT) diff --git a/src/FSharp.Control.TaskSeq/Utils.fs b/src/FSharp.Control.TaskSeq/Utils.fs index 5b35fcaa..63274ed4 100644 --- a/src/FSharp.Control.TaskSeq/Utils.fs +++ b/src/FSharp.Control.TaskSeq/Utils.fs @@ -115,52 +115,3 @@ module Async = /// Bind an Async<'T> let inline bind binder (task: Async<'T>) : Async<'U> = ExtraTopLevelOperators.async { return! binder task } - -type Debug = - - [] - static val mutable private verbose: bool option - - /// Setting from environment variable TASKSEQ_LOG_VERBOSE, which, - /// when set, enables (very) verbose printing of flow and state - static member private getVerboseSetting() = - match Debug.verbose with - | None -> - let verboseEnv = - try - match Environment.GetEnvironmentVariable "TASKSEQ_LOG_VERBOSE" with - | null -> false - | x -> - match x.ToLowerInvariant().Trim() with - | "1" - | "true" - | "on" - | "yes" -> true - | _ -> false - - with _ -> - false - - Debug.verbose <- Some verboseEnv - verboseEnv - - | Some setting -> setting - - /// Private helper to log to stdout in DEBUG builds only - [] - static member private print value = - match Debug.getVerboseSetting () with - | false -> () - | true -> - // don't use ksprintf here, because the compiler does not remove all allocations due to - // the way PrintfFormat types are compiled, even if we set the Conditional attribute. - let ct = Thread.CurrentThread - printfn "%i (%b): %s" ct.ManagedThreadId ct.IsThreadPoolThread value - - /// Log to stdout in DEBUG builds only - [] - static member logInfo(str) = Debug.print str - - /// Log to stdout in DEBUG builds only - [] - static member logInfo(str, data) = Debug.print $"%s{str}{data}" diff --git a/src/FSharp.Control.TaskSeq/Utils.fsi b/src/FSharp.Control.TaskSeq/Utils.fsi new file mode 100644 index 00000000..219d8e0a --- /dev/null +++ b/src/FSharp.Control.TaskSeq/Utils.fsi @@ -0,0 +1,87 @@ +namespace FSharp.Control + +open System.Diagnostics +open System.Threading.Tasks +open System.Threading.Tasks.Sources + +[] +module ValueTaskExtensions = + type System.Threading.Tasks.ValueTask with + + /// (Extension member) Gets a task that has already completed successfully. + static member inline CompletedTask: System.Threading.Tasks.ValueTask + +module ValueTask = + + /// A successfully completed ValueTask of boolean that has the value false. + val False: ValueTask + + /// A successfully completed ValueTask of boolean that has the value true. + val True: ValueTask + + /// Creates a ValueTask with the supplied result of the successful operation. + val inline FromResult: x: 'T -> ValueTask<'T> + + /// Creates a ValueTask with an IValueTaskSource representing the operation + val inline ofIValueTaskSource: taskSource: IValueTaskSource -> version: int16 -> ValueTask + + /// Creates a ValueTask form a Task<'T> + val inline ofTask: task: Task<'T> -> ValueTask<'T> + + /// Ignore a ValueTask<'T>, returns a non-generic ValueTask. + val inline ignore: vtask: ValueTask<'T> -> ValueTask + +module Task = + + /// Convert an Async<'T> into a Task<'T> + val inline ofAsync: async: Async<'T> -> Task<'T> + + /// Convert a unit-task into a Task + val inline ofTask: task': Task -> Task + + /// Convert a non-task function into a task-returning function + val inline apply: func: ('a -> 'b) -> ('a -> Task<'b>) + + /// Convert a Task<'T> into an Async<'T> + val inline toAsync: task: Task<'T> -> Async<'T> + + /// Convert a Task<'T> into a ValueTask<'T> + val inline toValueTask: task: Task<'T> -> ValueTask<'T> + + /// + /// Convert a ValueTask<'T> to a Task<'T>. To use a non-generic ValueTask, + /// consider using: . + /// + val inline ofValueTask: valueTask: ValueTask<'T> -> Task<'T> + + /// Convert a Task<'T> into a non-generic Task, ignoring the result + val inline ignore: task: Task<'T> -> Task + + /// Map a Task<'T> + val inline map: mapper: ('T -> 'U) -> task: Task<'T> -> Task<'U> + + /// Bind a Task<'T> + val inline bind: binder: ('T -> #Task<'U>) -> task: Task<'T> -> Task<'U> + + /// Create a task from a value + val inline fromResult: value: 'U -> Task<'U> + +module Async = + + /// Convert an Task<'T> into an Async<'T> + val inline ofTask: task: Task<'T> -> Async<'T> + + /// Convert a unit-task into an Async + val inline ofUnitTask: task: Task -> Async + + /// Convert a Task<'T> into an Async<'T> + val inline toTask: async: Async<'T> -> Task<'T> + + /// Convert an Async<'T> into an Async, ignoring the result + val inline ignore: async': Async<'T> -> Async + + /// Map an Async<'T> + val inline map: mapper: ('T -> 'U) -> async: Async<'T> -> Async<'U> + + /// Bind an Async<'T> + val inline bind: binder: (Async<'T> -> Async<'U>) -> task: Async<'T> -> Async<'U> From 36c166f2c8c9fa736aeb72d2c33d3679273858ac Mon Sep 17 00:00:00 2001 From: Abel Braaksma Date: Mon, 28 Nov 2022 00:01:04 +0100 Subject: [PATCH 2/5] Remove `TaskSeqResumptionFunc` and `TaskSeqResumptionDynamicInfo` from the public surface --- src/FSharp.Control.TaskSeq/TaskSeqBuilder.fsi | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/FSharp.Control.TaskSeq/TaskSeqBuilder.fsi b/src/FSharp.Control.TaskSeq/TaskSeqBuilder.fsi index bfd082f8..7040e2e9 100644 --- a/src/FSharp.Control.TaskSeq/TaskSeqBuilder.fsi +++ b/src/FSharp.Control.TaskSeq/TaskSeqBuilder.fsi @@ -97,8 +97,6 @@ and [] TaskSeq<'Machine, 'T and TaskSeqCode<'T> = ResumableCode, unit> and TaskSeqStateMachine<'T> = ResumableStateMachine> -and TaskSeqResumptionFunc<'T> = ResumptionFunc> -and TaskSeqResumptionDynamicInfo<'T> = ResumptionDynamicInfo> [] type TaskSeqBuilder = From a1013f2de4414a047a2b4cb79cbc8da3fd72df2b Mon Sep 17 00:00:00 2001 From: Abel Braaksma Date: Mon, 28 Nov 2022 03:22:28 +0100 Subject: [PATCH 3/5] Update descriptions and add warnings for use of internal-only public types --- src/FSharp.Control.TaskSeq/TaskSeqBuilder.fs | 66 +++++----- src/FSharp.Control.TaskSeq/TaskSeqBuilder.fsi | 113 +++++++++++------- 2 files changed, 102 insertions(+), 77 deletions(-) diff --git a/src/FSharp.Control.TaskSeq/TaskSeqBuilder.fs b/src/FSharp.Control.TaskSeq/TaskSeqBuilder.fs index a9d4d52f..bdf5c725 100644 --- a/src/FSharp.Control.TaskSeq/TaskSeqBuilder.fs +++ b/src/FSharp.Control.TaskSeq/TaskSeqBuilder.fs @@ -19,8 +19,6 @@ open FSharp.Control [] module Internal = // cannot be marked with 'internal' scope - /// Setting from environment variable TASKSEQ_LOG_VERBOSE, which, - /// when set, enables (very) verbose printing of flow and state let initVerbose () = try match Environment.GetEnvironmentVariable "TASKSEQ_LOG_VERBOSE" with @@ -37,10 +35,8 @@ module Internal = // cannot be marked with 'internal' scope false - /// Call MoveNext on an IAsyncStateMachine by reference let inline moveNextRef (x: byref<'T> when 'T :> IAsyncStateMachine) = x.MoveNext() - /// F# requires that we implement interfaces even on an abstract class let inline raiseNotImpl () = NotImplementedException "Abstract Class: method or property not implemented" |> raise @@ -79,7 +75,7 @@ type TaskSeqStateMachineData<'T>() = /// A reference to 'self', because otherwise we can't use byref in the resumable code. [] - val mutable boxedSelf: TaskSeq<'T> + val mutable boxedSelf: TaskSeqBase<'T> member data.PushDispose(disposer: unit -> Task) = if isNull data.disposalStack then @@ -91,7 +87,7 @@ type TaskSeqStateMachineData<'T>() = if not (isNull data.disposalStack) then data.disposalStack.RemoveAt(data.disposalStack.Count - 1) -and [] TaskSeq<'T>() = +and [] TaskSeqBase<'T>() = abstract MoveNextAsyncResult: unit -> ValueTask @@ -119,7 +115,7 @@ and [] TaskSeq<'T>() = and [] TaskSeq<'Machine, 'T when 'Machine :> IAsyncStateMachine and 'Machine :> IResumableStateMachine>>() = - inherit TaskSeq<'T>() + inherit TaskSeqBase<'T>() let initialThreadId = Environment.CurrentManagedThreadId /// Shadows the initial machine, just after it is initialized by the F# compiler-generated state. @@ -304,16 +300,16 @@ and [] TaskSeq<'Machine, 'T // assume it's a possibly new, not yet supported case, treat as default ValueTask.ofIValueTaskSource this version -and TaskSeqCode<'T> = ResumableCode, unit> +and ResumableTSC<'T> = ResumableCode, unit> and TaskSeqStateMachine<'T> = ResumableStateMachine> and TaskSeqResumptionFunc<'T> = ResumptionFunc> and TaskSeqResumptionDynamicInfo<'T> = ResumptionDynamicInfo> type TaskSeqBuilder() = - member inline _.Delay(f: unit -> TaskSeqCode<'T>) : TaskSeqCode<'T> = TaskSeqCode<'T>(fun sm -> f().Invoke(&sm)) + member inline _.Delay(f: unit -> ResumableTSC<'T>) : ResumableTSC<'T> = ResumableTSC<'T>(fun sm -> f().Invoke(&sm)) - member inline _.Run(code: TaskSeqCode<'T>) : IAsyncEnumerable<'T> = + member inline _.Run(code: ResumableTSC<'T>) : IAsyncEnumerable<'T> = if __useResumableCode then // This is the static implementation. A new struct type is created. __stateMachine, IAsyncEnumerable<'T>> @@ -386,11 +382,11 @@ type TaskSeqBuilder() = |> raise - member inline _.Zero() : TaskSeqCode<'T> = + member inline _.Zero() : ResumableTSC<'T> = Debug.logInfo "at Zero()" ResumableCode.Zero() - member inline _.Combine(task1: TaskSeqCode<'T>, task2: TaskSeqCode<'T>) : TaskSeqCode<'T> = + member inline _.Combine(task1: ResumableTSC<'T>, task2: ResumableTSC<'T>) : ResumableTSC<'T> = Debug.logInfo "at Combine(.., ..)" ResumableCode.Combine(task1, task2) @@ -399,8 +395,8 @@ type TaskSeqBuilder() = member inline _.WhileAsync ( [] condition: unit -> ValueTask, - body: TaskSeqCode<'T> - ) : TaskSeqCode<'T> = + body: ResumableTSC<'T> + ) : ResumableTSC<'T> = let mutable condition_res = true ResumableCode.While( @@ -436,17 +432,17 @@ type TaskSeqBuilder() = false) ) - member inline b.While([] condition: unit -> bool, body: TaskSeqCode<'T>) : TaskSeqCode<'T> = + member inline b.While([] condition: unit -> bool, body: ResumableTSC<'T>) : ResumableTSC<'T> = Debug.logInfo "at While(...)" ResumableCode.While(condition, body) - member inline _.TryWith(body: TaskSeqCode<'T>, catch: exn -> TaskSeqCode<'T>) : TaskSeqCode<'T> = + member inline _.TryWith(body: ResumableTSC<'T>, catch: exn -> ResumableTSC<'T>) : ResumableTSC<'T> = ResumableCode.TryWith(body, catch) - member inline _.TryFinallyAsync(body: TaskSeqCode<'T>, compensation: unit -> Task) : TaskSeqCode<'T> = + member inline _.TryFinallyAsync(body: ResumableTSC<'T>, compensation: unit -> Task) : ResumableTSC<'T> = ResumableCode.TryFinallyAsync( - TaskSeqCode<'T>(fun sm -> + ResumableTSC<'T>(fun sm -> sm.Data.PushDispose(fun () -> compensation ()) body.Invoke(&sm)), @@ -467,9 +463,9 @@ type TaskSeqBuilder() = __stack_condition_fin) ) - member inline _.TryFinally(body: TaskSeqCode<'T>, compensation: unit -> unit) : TaskSeqCode<'T> = + member inline _.TryFinally(body: ResumableTSC<'T>, compensation: unit -> unit) : ResumableTSC<'T> = ResumableCode.TryFinally( - TaskSeqCode<'T>(fun sm -> + ResumableTSC<'T>(fun sm -> sm.Data.PushDispose(fun () -> compensation () Task.CompletedTask) @@ -482,7 +478,7 @@ type TaskSeqBuilder() = true) ) - member inline this.Using(disp: #IAsyncDisposable, body: #IAsyncDisposable -> TaskSeqCode<'T>) : TaskSeqCode<'T> = + member inline this.Using(disp: #IAsyncDisposable, body: #IAsyncDisposable -> ResumableTSC<'T>) : ResumableTSC<'T> = // A using statement is just a try/finally with the finally block disposing if non-null. this.TryFinallyAsync( @@ -494,8 +490,8 @@ type TaskSeqBuilder() = Task.CompletedTask) ) - member inline _.Yield(v: 'T) : TaskSeqCode<'T> = - TaskSeqCode<'T>(fun sm -> + member inline _.Yield(v: 'T) : ResumableTSC<'T> = + ResumableTSC<'T>(fun sm -> // This will yield with __stack_fin = false // This will resume with __stack_fin = true Debug.logInfo "at Yield" @@ -546,10 +542,10 @@ module LowPriority = and ^Awaiter: (member GetResult: unit -> 'TResult1)> ( task: ^TaskLike, - continuation: ('TResult1 -> TaskSeqCode<'TResult2>) - ) : TaskSeqCode<'TResult2> = + continuation: ('TResult1 -> ResumableTSC<'TResult2>) + ) : ResumableTSC<'TResult2> = - TaskSeqCode<'TResult2>(fun sm -> + ResumableTSC<'TResult2>(fun sm -> let mutable awaiter = (^TaskLike: (member GetAwaiter: unit -> ^Awaiter) (task)) let mutable __stack_fin = true @@ -581,7 +577,7 @@ module LowPriority = module MediumPriority = type TaskSeqBuilder with - member inline this.Using(disp: #IDisposable, body: #IDisposable -> TaskSeqCode<'T>) : TaskSeqCode<'T> = + member inline this.Using(disp: #IDisposable, body: #IDisposable -> ResumableTSC<'T>) : ResumableTSC<'T> = // A using statement is just a try/finally with the finally block disposing if non-null. this.TryFinally( @@ -592,7 +588,7 @@ module MediumPriority = disp.Dispose()) ) - member inline this.For(sequence: seq<'TElement>, body: 'TElement -> TaskSeqCode<'T>) : TaskSeqCode<'T> = + member inline this.For(sequence: seq<'TElement>, body: 'TElement -> ResumableTSC<'T>) : ResumableTSC<'T> = // A for loop is just a using statement on the sequence's enumerator... this.Using( sequence.GetEnumerator(), @@ -600,14 +596,14 @@ module MediumPriority = (fun e -> this.While((fun () -> e.MoveNext()), (fun sm -> (body e.Current).Invoke(&sm)))) ) - member inline this.YieldFrom(source: seq<'T>) : TaskSeqCode<'T> = this.For(source, (fun v -> this.Yield(v))) + member inline this.YieldFrom(source: seq<'T>) : ResumableTSC<'T> = this.For(source, (fun v -> this.Yield(v))) member inline this.For ( source: #IAsyncEnumerable<'TElement>, - body: 'TElement -> TaskSeqCode<'T> - ) : TaskSeqCode<'T> = - TaskSeqCode<'T>(fun sm -> + body: 'TElement -> ResumableTSC<'T> + ) : ResumableTSC<'T> = + ResumableTSC<'T>(fun sm -> this .Using( source.GetAsyncEnumerator(sm.Data.cancellationToken), @@ -616,7 +612,7 @@ module MediumPriority = ) .Invoke(&sm)) - member inline this.YieldFrom(source: IAsyncEnumerable<'T>) : TaskSeqCode<'T> = + member inline this.YieldFrom(source: IAsyncEnumerable<'T>) : ResumableTSC<'T> = this.For(source, (fun v -> this.Yield(v))) [] @@ -633,8 +629,8 @@ module HighPriority = // - In contrast, ValueTask<_> *does have* GetResult() -> 'TResult // - Conclusion: we do not need an extra overload anymore for ValueTask // - member inline _.Bind(task: Task<'TResult1>, continuation: ('TResult1 -> TaskSeqCode<'T>)) : TaskSeqCode<'T> = - TaskSeqCode<'T>(fun sm -> + member inline _.Bind(task: Task<'TResult1>, continuation: ('TResult1 -> ResumableTSC<'T>)) : ResumableTSC<'T> = + ResumableTSC<'T>(fun sm -> let mutable awaiter = task.GetAwaiter() let mutable __stack_fin = true diff --git a/src/FSharp.Control.TaskSeq/TaskSeqBuilder.fsi b/src/FSharp.Control.TaskSeq/TaskSeqBuilder.fsi index 7040e2e9..4f115910 100644 --- a/src/FSharp.Control.TaskSeq/TaskSeqBuilder.fsi +++ b/src/FSharp.Control.TaskSeq/TaskSeqBuilder.fsi @@ -12,19 +12,37 @@ open FSharp.Core.CompilerServices [] module Internal = - /// Setting from environment variable TASKSEQ_LOG_VERBOSE, which, + /// + /// Setting from environment variable , which, /// when set, enables (very) verbose printing of flow and state + /// val initVerbose: unit -> bool /// Call MoveNext on an IAsyncStateMachine by reference val inline moveNextRef: x: byref<#IAsyncStateMachine> -> unit + /// F# requires that we implement interfaces even on an abstract class. val inline raiseNotImpl: unit -> 'a +/// +/// Result of any computation expression, alias for . +/// type taskSeq<'T> = IAsyncEnumerable<'T> -[] -type TaskSeqStateMachineData<'T> = +/// TaskSeqCode type alias of ResumableCode delegate type, specially recognized by the F# compiler +and ResumableTSC<'T> = ResumableCode, unit> + +/// +/// Contains the state data for the computation expression builder. +/// For use in this library only. Required by the method. +/// +and TaskSeqStateMachine<'T> = ResumableStateMachine> + +/// +/// Contains the state data for the computation expression builder. +/// For use in this library only. Required by the method. +/// +and [] TaskSeqStateMachineData<'T> = new: unit -> TaskSeqStateMachineData<'T> @@ -56,26 +74,35 @@ type TaskSeqStateMachineData<'T> = /// A reference to 'self', because otherwise we can't use byref in the resumable code. [] - val mutable boxedSelf: TaskSeq<'T> + val mutable boxedSelf: TaskSeqBase<'T> member PopDispose: unit -> unit member PushDispose: disposer: (unit -> Task) -> unit -and [] TaskSeq<'T> = +/// +/// Abstract base class for . +/// For use by this library only, should not be used directly in user code. Its operation depends highly on resumable state. +/// +and [] TaskSeqBase<'T> = interface IValueTaskSource interface IValueTaskSource interface IAsyncStateMachine interface IAsyncEnumerable<'T> interface IAsyncEnumerator<'T> - new: unit -> TaskSeq<'T> + new: unit -> TaskSeqBase<'T> abstract MoveNextAsyncResult: unit -> ValueTask +/// +/// Main implementation of generic and related interfaces, +/// which forms the meat of the logic behind computation expresssions. +/// For use by this library only, should not be used directly in user code. Its operation depends highly on resumable state. +/// and [] TaskSeq<'Machine, 'T when 'Machine :> IAsyncStateMachine and 'Machine :> IResumableStateMachine>> = - inherit TaskSeq<'T> + inherit TaskSeqBase<'T> interface IAsyncEnumerator<'T> interface IAsyncEnumerable<'T> interface IAsyncStateMachine @@ -95,24 +122,24 @@ and [] TaskSeq<'Machine, 'T member InitMachineData: ct: CancellationToken * machine: byref<'Machine> -> unit override MoveNextAsyncResult: unit -> ValueTask -and TaskSeqCode<'T> = ResumableCode, unit> -and TaskSeqStateMachine<'T> = ResumableStateMachine> - +/// +/// Main builder class for the computation expression. +/// [] type TaskSeqBuilder = - member inline Combine: task1: TaskSeqCode<'T> * task2: TaskSeqCode<'T> -> TaskSeqCode<'T> - member inline Delay: f: (unit -> TaskSeqCode<'T>) -> TaskSeqCode<'T> - member inline Run: code: TaskSeqCode<'T> -> IAsyncEnumerable<'T> - member inline TryFinally: body: TaskSeqCode<'T> * compensation: (unit -> unit) -> TaskSeqCode<'T> - member inline TryFinallyAsync: body: TaskSeqCode<'T> * compensation: (unit -> Task) -> TaskSeqCode<'T> - member inline TryWith: body: TaskSeqCode<'T> * catch: (exn -> TaskSeqCode<'T>) -> TaskSeqCode<'T> - member inline Using: disp: 'a * body: ('a -> TaskSeqCode<'T>) -> TaskSeqCode<'T> when 'a :> IAsyncDisposable - member inline While: condition: (unit -> bool) * body: TaskSeqCode<'T> -> TaskSeqCode<'T> + member inline Combine: task1: ResumableTSC<'T> * task2: ResumableTSC<'T> -> ResumableTSC<'T> + member inline Delay: f: (unit -> ResumableTSC<'T>) -> ResumableTSC<'T> + member inline Run: code: ResumableTSC<'T> -> taskSeq<'T> + member inline TryFinally: body: ResumableTSC<'T> * compensation: (unit -> unit) -> ResumableTSC<'T> + member inline TryFinallyAsync: body: ResumableTSC<'T> * compensation: (unit -> Task) -> ResumableTSC<'T> + member inline TryWith: body: ResumableTSC<'T> * catch: (exn -> ResumableTSC<'T>) -> ResumableTSC<'T> + member inline Using: disp: 'a * body: ('a -> ResumableTSC<'T>) -> ResumableTSC<'T> when 'a :> IAsyncDisposable + member inline While: condition: (unit -> bool) * body: ResumableTSC<'T> -> ResumableTSC<'T> /// Used by `For`. F# currently doesn't support `while!`, so this cannot be called directly from the CE - member inline WhileAsync: condition: (unit -> ValueTask) * body: TaskSeqCode<'T> -> TaskSeqCode<'T> - member inline Yield: v: 'T -> TaskSeqCode<'T> - member inline Zero: unit -> TaskSeqCode<'T> + member inline WhileAsync: condition: (unit -> ValueTask) * body: ResumableTSC<'T> -> ResumableTSC<'T> + member inline Yield: v: 'T -> ResumableTSC<'T> + member inline Zero: unit -> ResumableTSC<'T> [] module TaskSeqBuilder = @@ -122,43 +149,45 @@ module TaskSeqBuilder = /// val taskSeq: TaskSeqBuilder +/// +/// Contains low priority extension methods for the main builder class for the computation expression. +/// The , and modules are not meant to be +/// accessed directly from user code. They solely serve to disambiguate overload resolution inside the computation expression. +/// [] module LowPriority = type TaskSeqBuilder with [] member inline Bind< ^TaskLike, 'TResult1, 'TResult2, ^Awaiter, 'TOverall> : - task: ^TaskLike * continuation: ('TResult1 -> TaskSeqCode<'TResult2>) -> TaskSeqCode<'TResult2> + task: ^TaskLike * continuation: ('TResult1 -> ResumableTSC<'TResult2>) -> ResumableTSC<'TResult2> when ^TaskLike: (member GetAwaiter: unit -> ^Awaiter) and ^Awaiter :> ICriticalNotifyCompletion and ^Awaiter: (member get_IsCompleted: unit -> bool) and ^Awaiter: (member GetResult: unit -> 'TResult1) +/// +/// Contains low priority extension methods for the main builder class for the computation expression. +/// The , and modules are not meant to be +/// accessed directly from user code. They solely serve to disambiguate overload resolution inside the computation expression. +/// [] module MediumPriority = type TaskSeqBuilder with - member inline Using: disp: 'a * body: ('a -> TaskSeqCode<'T>) -> TaskSeqCode<'T> when 'a :> IDisposable - - type TaskSeqBuilder with - - member inline For: sequence: seq<'TElement> * body: ('TElement -> TaskSeqCode<'T>) -> TaskSeqCode<'T> - - type TaskSeqBuilder with - - member inline YieldFrom: source: seq<'T> -> TaskSeqCode<'T> - - type TaskSeqBuilder with - - member inline For: - source: #IAsyncEnumerable<'TElement> * body: ('TElement -> TaskSeqCode<'T>) -> TaskSeqCode<'T> - - type TaskSeqBuilder with - - member inline YieldFrom: source: IAsyncEnumerable<'T> -> TaskSeqCode<'T> - + member inline Using: disp: 'a * body: ('a -> ResumableTSC<'T>) -> ResumableTSC<'T> when 'a :> IDisposable + member inline For: sequence: seq<'TElement> * body: ('TElement -> ResumableTSC<'T>) -> ResumableTSC<'T> + member inline YieldFrom: source: seq<'T> -> ResumableTSC<'T> + member inline For: source: #taskSeq<'TElement> * body: ('TElement -> ResumableTSC<'T>) -> ResumableTSC<'T> + member inline YieldFrom: source: taskSeq<'T> -> ResumableTSC<'T> + +/// +/// Contains low priority extension methods for the main builder class for the computation expression. +/// The , and modules are not meant to be +/// accessed directly from user code. They solely serve to disambiguate overload resolution inside the computation expression. +/// [] module HighPriority = type TaskSeqBuilder with - member inline Bind: task: Task<'TResult1> * continuation: ('TResult1 -> TaskSeqCode<'T>) -> TaskSeqCode<'T> + member inline Bind: task: Task<'TResult1> * continuation: ('TResult1 -> ResumableTSC<'T>) -> ResumableTSC<'T> From 554f189c01bf1f494105ecff9b6ac217229d001b Mon Sep 17 00:00:00 2001 From: Abel Braaksma Date: Mon, 28 Nov 2022 03:25:44 +0100 Subject: [PATCH 4/5] Revert changes to `TaskSeq.fsi` --- src/FSharp.Control.TaskSeq/TaskSeq.fsi | 888 ++++++++++++++++--------- 1 file changed, 568 insertions(+), 320 deletions(-) diff --git a/src/FSharp.Control.TaskSeq/TaskSeq.fsi b/src/FSharp.Control.TaskSeq/TaskSeq.fsi index 47f1b1e4..1f0f1497 100644 --- a/src/FSharp.Control.TaskSeq/TaskSeq.fsi +++ b/src/FSharp.Control.TaskSeq/TaskSeq.fsi @@ -1,322 +1,570 @@ namespace FSharp.Control - - module TaskSeq = - - val empty<'T> : System.Collections.Generic.IAsyncEnumerable<'T> - - val singleton: - source: 'T -> System.Collections.Generic.IAsyncEnumerable<'T> - - val isEmpty: source: taskSeq<'T> -> System.Threading.Tasks.Task - - val toList: source: taskSeq<'T> -> 'T list - - val toArray: source: taskSeq<'T> -> 'T[] - - val toSeq: source: taskSeq<'T> -> seq<'T> - - val toArrayAsync: - source: taskSeq<'T> -> System.Threading.Tasks.Task<'T[]> - - val toListAsync: - source: taskSeq<'T> -> System.Threading.Tasks.Task<'T list> - - val toResizeArrayAsync: - source: taskSeq<'T> -> System.Threading.Tasks.Task> - - val toIListAsync: - source: taskSeq<'T> -> - System.Threading.Tasks.Task> - - val ofArray: - source: 'T[] -> System.Collections.Generic.IAsyncEnumerable<'T> - - val ofList: - source: 'T list -> System.Collections.Generic.IAsyncEnumerable<'T> - - val ofSeq: - source: seq<'T> -> System.Collections.Generic.IAsyncEnumerable<'T> - - val ofResizeArray: - source: ResizeArray<'T> -> - System.Collections.Generic.IAsyncEnumerable<'T> - - val ofTaskSeq: - source: seq<#System.Threading.Tasks.Task<'T>> -> - System.Collections.Generic.IAsyncEnumerable<'T> - - val ofTaskList: - source: #System.Threading.Tasks.Task<'T> list -> - System.Collections.Generic.IAsyncEnumerable<'T> - - val ofTaskArray: - source: #System.Threading.Tasks.Task<'T> array -> - System.Collections.Generic.IAsyncEnumerable<'T> - - val ofAsyncSeq: - source: seq> -> - System.Collections.Generic.IAsyncEnumerable<'T> - - val ofAsyncList: - source: Async<'T> list -> - System.Collections.Generic.IAsyncEnumerable<'T> - - val ofAsyncArray: - source: Async<'T> array -> - System.Collections.Generic.IAsyncEnumerable<'T> - - val length: source: taskSeq<'T> -> System.Threading.Tasks.Task - - val lengthOrMax: - max: int -> source: taskSeq<'T> -> System.Threading.Tasks.Task - - val lengthBy: - predicate: ('T -> bool) -> - source: taskSeq<'T> -> System.Threading.Tasks.Task - - val lengthByAsync: - predicate: ('T -> #System.Threading.Tasks.Task) -> - source: taskSeq<'T> -> System.Threading.Tasks.Task - - val init: - count: int -> - initializer: (int -> 'T) -> - System.Collections.Generic.IAsyncEnumerable<'T> - - val initInfinite: - initializer: (int -> 'T) -> - System.Collections.Generic.IAsyncEnumerable<'T> - - val initAsync: - count: int -> - initializer: (int -> #System.Threading.Tasks.Task<'T>) -> - System.Collections.Generic.IAsyncEnumerable<'T> - - val initInfiniteAsync: - initializer: (int -> #System.Threading.Tasks.Task<'T>) -> - System.Collections.Generic.IAsyncEnumerable<'T> - - val delay: - generator: (unit -> taskSeq<'T>) -> - System.Collections.Generic.IAsyncEnumerable<'T> - - val concat: - sources: taskSeq<#taskSeq<'T>> -> - System.Collections.Generic.IAsyncEnumerable<'T> - - val append: - source1: #taskSeq<'T> -> - source2: #taskSeq<'T> -> - System.Collections.Generic.IAsyncEnumerable<'T> - - val appendSeq: - source1: #taskSeq<'T> -> - source2: #seq<'T> -> System.Collections.Generic.IAsyncEnumerable<'T> - - val prependSeq: - source1: #seq<'T> -> - source2: #taskSeq<'T> -> - System.Collections.Generic.IAsyncEnumerable<'T> - - val cast: source: taskSeq -> taskSeq<'T> - - val box: - source: taskSeq<'T> -> - System.Collections.Generic.IAsyncEnumerable - - val unbox: source: taskSeq -> taskSeq<'U> when 'U: struct - - val iter: - action: ('T -> unit) -> - source: taskSeq<'T> -> System.Threading.Tasks.Task - - val iteri: - action: (int -> 'T -> unit) -> - source: taskSeq<'T> -> System.Threading.Tasks.Task - - val iterAsync: - action: ('T -> #System.Threading.Tasks.Task) -> - source: taskSeq<'T> -> System.Threading.Tasks.Task - - val iteriAsync: - action: (int -> 'T -> #System.Threading.Tasks.Task) -> - source: taskSeq<'T> -> System.Threading.Tasks.Task - - val map: - mapper: ('T -> 'U) -> - source: taskSeq<'T> -> - System.Collections.Generic.IAsyncEnumerable<'U> - - val mapi: - mapper: (int -> 'T -> 'U) -> - source: taskSeq<'T> -> - System.Collections.Generic.IAsyncEnumerable<'U> - - val mapAsync: - mapper: ('T -> #System.Threading.Tasks.Task<'U>) -> - source: taskSeq<'T> -> - System.Collections.Generic.IAsyncEnumerable<'U> - - val mapiAsync: - mapper: (int -> 'T -> #System.Threading.Tasks.Task<'U>) -> - source: taskSeq<'T> -> - System.Collections.Generic.IAsyncEnumerable<'U> - - val collect: - binder: ('T -> #System.Collections.Generic.IAsyncEnumerable<'U>) -> - source: taskSeq<'T> -> - System.Collections.Generic.IAsyncEnumerable<'U> - - val collectSeq: - binder: ('T -> #seq<'U>) -> - source: taskSeq<'T> -> - System.Collections.Generic.IAsyncEnumerable<'U> - - val collectAsync: - binder: ('T -> #System.Threading.Tasks.Task<'TSeqU>) -> - source: taskSeq<'T> -> taskSeq<'U> - when 'TSeqU :> System.Collections.Generic.IAsyncEnumerable<'U> - - val collectSeqAsync: - binder: ('T -> #System.Threading.Tasks.Task<'SeqU>) -> - source: taskSeq<'T> -> taskSeq<'U> when 'SeqU :> seq<'U> - - val tryHead: - source: taskSeq<'T> -> System.Threading.Tasks.Task<'T option> - - val head: source: taskSeq<'T> -> System.Threading.Tasks.Task<'T> - - val tryLast: - source: taskSeq<'T> -> System.Threading.Tasks.Task<'T option> - - val last: source: taskSeq<'T> -> System.Threading.Tasks.Task<'T> - - val tryTail: - source: taskSeq<'T> -> - System.Threading.Tasks.Task option> - - val tail: - source: taskSeq<'T> -> - System.Threading.Tasks.Task> - - val tryItem: - index: int -> - source: taskSeq<'T> -> System.Threading.Tasks.Task<'T option> - - val item: - index: int -> source: taskSeq<'T> -> System.Threading.Tasks.Task<'T> - - val tryExactlyOne: - source: taskSeq<'T> -> System.Threading.Tasks.Task<'T option> - - val exactlyOne: source: taskSeq<'T> -> System.Threading.Tasks.Task<'T> - - val indexed: - source: taskSeq<'T> -> - System.Collections.Generic.IAsyncEnumerable - - val choose: - chooser: ('T -> 'U option) -> - source: taskSeq<'T> -> - System.Collections.Generic.IAsyncEnumerable<'U> - - val chooseAsync: - chooser: ('T -> #System.Threading.Tasks.Task<'U option>) -> - source: taskSeq<'T> -> - System.Collections.Generic.IAsyncEnumerable<'U> - - val filter: - predicate: ('T -> bool) -> - source: taskSeq<'T> -> - System.Collections.Generic.IAsyncEnumerable<'T> - - val filterAsync: - predicate: ('T -> #System.Threading.Tasks.Task) -> - source: taskSeq<'T> -> - System.Collections.Generic.IAsyncEnumerable<'T> - - val tryPick: - chooser: ('T -> 'U option) -> - source: taskSeq<'T> -> System.Threading.Tasks.Task<'U option> - - val tryPickAsync: - chooser: ('T -> #System.Threading.Tasks.Task<'U option>) -> - source: taskSeq<'T> -> System.Threading.Tasks.Task<'U option> - - val tryFind: - predicate: ('T -> bool) -> - source: taskSeq<'T> -> System.Threading.Tasks.Task<'T option> - - val tryFindAsync: - predicate: ('T -> #System.Threading.Tasks.Task) -> - source: taskSeq<'T> -> System.Threading.Tasks.Task<'T option> - - val tryFindIndex: - predicate: ('T -> bool) -> - source: taskSeq<'T> -> System.Threading.Tasks.Task - - val tryFindIndexAsync: - predicate: ('T -> #System.Threading.Tasks.Task) -> - source: taskSeq<'T> -> System.Threading.Tasks.Task - - val except: - itemsToExclude: taskSeq<'T> -> - source: taskSeq<'T> -> - System.Collections.Generic.IAsyncEnumerable<'T> when 'T: equality - - val exceptOfSeq: - itemsToExclude: seq<'T> -> - source: taskSeq<'T> -> - System.Collections.Generic.IAsyncEnumerable<'T> when 'T: equality - - val exists: - predicate: ('T -> bool) -> - source: taskSeq<'T> -> System.Threading.Tasks.Task - - val existsAsync: - predicate: ('T -> #System.Threading.Tasks.Task) -> - source: taskSeq<'T> -> System.Threading.Tasks.Task - - val contains: - value: 'T -> source: taskSeq<'T> -> System.Threading.Tasks.Task - when 'T: equality - - val pick: - chooser: ('T -> 'U option) -> - source: taskSeq<'T> -> System.Threading.Tasks.Task<'U> - - val pickAsync: - chooser: ('T -> #System.Threading.Tasks.Task<'U option>) -> - source: taskSeq<'T> -> System.Threading.Tasks.Task<'U> - - val find: - predicate: ('T -> bool) -> - source: taskSeq<'T> -> System.Threading.Tasks.Task<'T> - - val findAsync: - predicate: ('T -> #System.Threading.Tasks.Task) -> - source: taskSeq<'T> -> System.Threading.Tasks.Task<'T> - - val findIndex: - predicate: ('T -> bool) -> - source: taskSeq<'T> -> System.Threading.Tasks.Task - - val findIndexAsync: - predicate: ('T -> #System.Threading.Tasks.Task) -> - source: taskSeq<'T> -> System.Threading.Tasks.Task - - val zip: - source1: taskSeq<'T> -> - source2: taskSeq<'U> -> - System.Collections.Generic.IAsyncEnumerable<'T * 'U> - - val fold: - folder: ('State -> 'T -> 'State) -> - state: 'State -> - source: taskSeq<'T> -> System.Threading.Tasks.Task<'State> - - val foldAsync: - folder: ('State -> 'T -> #System.Threading.Tasks.Task<'State>) -> - state: 'State -> - source: taskSeq<'T> -> System.Threading.Tasks.Task<'State> +#nowarn "1204" + +module TaskSeq = + open System.Collections.Generic + open System.Threading.Tasks + + /// Initialize an empty taskSeq. + val empty<'T> : taskSeq<'T> + + /// + /// Creates a sequence from that generates a single element and then ends. + /// + val singleton: source: 'T -> taskSeq<'T> + + /// + /// Returns if the task sequence contains no elements, otherwise. + /// + val isEmpty: source: taskSeq<'T> -> Task + + /// + /// Returns the length of the sequence. This operation requires the whole sequence to be evaluated and + /// should not be used on potentially infinite sequences, see for an alternative. + /// + val length: source: taskSeq<'T> -> Task + + /// + /// Returns the length of the sequence, or , whichever comes first. This operation requires the task sequence + /// to be evaluated in full, or until items have been processed. Use this method instead of + /// if you want to prevent too many items to be evaluated, or if the sequence is potentially infinite. + /// + val lengthOrMax: max: int -> source: taskSeq<'T> -> Task + + /// + /// Returns the length of the sequence of all items for which the returns true. + /// This operation requires the whole sequence to be evaluated and should not be used on potentially infinite sequences. + /// + val lengthBy: predicate: ('T -> bool) -> source: taskSeq<'T> -> Task + + /// + /// Returns the length of the sequence of all items for which the returns true. + /// This operation requires the whole sequence to be evaluated and should not be used on potentially infinite sequences. + /// If does not need to be asynchronous, consider using . + /// + val lengthByAsync: predicate: ('T -> #Task) -> source: taskSeq<'T> -> Task + + /// + /// Returns a task sequence that is given by the delayed specification of a task sequence. + /// + /// + /// The generating function for the task sequence. + /// The generated task sequence. + val delay: generator: (unit -> taskSeq<'T>) -> taskSeq<'T> + + /// + /// Generates a new task sequence which, when iterated, will return successive elements by calling the given function + /// with the current index, up to the given count. Each element is saved after its initialization for successive access to + /// , which will not re-evaluate the . However, + /// re-iterating the returned task sequence will re-evaluate the initialization function. The returned sequence may + /// be passed between threads safely. However, individual IEnumerator values generated from the returned sequence should + /// not be accessed concurrently. + /// + /// + /// The maximum number of items to generate for the sequence. + /// A function that generates an item in the sequence from a given index. + /// The resulting task sequence. + /// Thrown when count is negative. + val init: count: int -> initializer: (int -> 'T) -> taskSeq<'T> + + /// + /// Generates a new task sequence which, when iterated, will return successive elements by calling the given function + /// with the current index, up to the given count. Each element is saved after its initialization for successive access to + /// , which will not re-evaluate the . However, + /// re-iterating the returned task sequence will re-evaluate the initialization function. The returned sequence may + /// be passed between threads safely. However, individual IEnumerator values generated from the returned sequence should + /// not be accessed concurrently. + /// + /// + /// The maximum number of items to generate for the sequence. + /// A function that generates an item in the sequence from a given index. + /// The resulting task sequence. + /// Thrown when count is negative. + val initAsync: count: int -> initializer: (int -> #Task<'T>) -> taskSeq<'T> + + /// + /// Generates a new task sequence which, when iterated, will return successive elements by calling the given function + /// with the current index, ad infinitum, or until is reached. + /// Each element is saved after its initialization for successive access to + /// , which will not re-evaluate the . However, + /// re-iterating the returned task sequence will re-evaluate the initialization function. The returned sequence may + /// be passed between threads safely. However, individual IEnumerator values generated from the returned sequence should + /// not be accessed concurrently. + /// + /// + /// A function that generates an item in the sequence from a given index. + /// The resulting task sequence. + val initInfinite: initializer: (int -> 'T) -> taskSeq<'T> + + /// + /// Generates a new task sequence which, when iterated, will return successive elements by calling the given function + /// with the current index, ad infinitum, or until is reached. + /// Each element is saved after its initialization for successive access to + /// , which will not re-evaluate the . However, + /// re-iterating the returned task sequence will re-evaluate the initialization function. The returned sequence may + /// be passed between threads safely. However, individual IEnumerator values generated from the returned sequence should + /// not be accessed concurrently. + /// + /// + /// A function that generates an item in the sequence from a given index. + /// The resulting task sequence. + val initInfiniteAsync: initializer: (int -> #Task<'T>) -> taskSeq<'T> + + /// + /// Combines the given task sequence of task sequences and concatenates them end-to-end, to form a + /// new flattened, single task sequence. Each task sequence is awaited item by item, before the next is iterated. + /// + /// + /// The input enumeration-of-enumerations. + /// The resulting task sequence. + /// Thrown when the input sequence is null. + val concat: sources: taskSeq<#taskSeq<'T>> -> taskSeq<'T> + + /// + /// Concatenates task sequences and in order as a single + /// task sequence. + /// + /// + /// The first input task sequence. + /// The second input task sequence. + /// The resulting task sequence. + /// Thrown when either of the input sequences is null. + val append: source1: #taskSeq<'T> -> source2: #taskSeq<'T> -> taskSeq<'T> + + /// + /// Concatenates a task sequence with a non-async F# in + /// and returns a single task sequence. + /// + /// + /// The input task sequence. + /// The input F# sequence. + /// The resulting task sequence. + /// Thrown when either of the input sequences is null. + val appendSeq: source1: #taskSeq<'T> -> source2: #seq<'T> -> taskSeq<'T> + + /// + /// Concatenates a non-async F# in with a task sequence in + /// and returns a single task sequence. + /// + /// + /// The input F# sequence. + /// The input task sequence. + /// The resulting task sequence. + /// Thrown when either of the input sequences is null. + val prependSeq: source1: #seq<'T> -> source2: #taskSeq<'T> -> taskSeq<'T> + + /// Returns taskSeq as an array. This function is blocking until the sequence is exhausted and will properly dispose of the resources. + val toList: source: taskSeq<'T> -> 'T list + + /// Returns taskSeq as an array. This function is blocking until the sequence is exhausted and will properly dispose of the resources. + val toArray: source: taskSeq<'T> -> 'T[] + + /// + /// Returns the task sequence as an F# , that is, an + /// . This function is blocking at each , but otherwise + /// acts as a normal delay-executed sequence. + /// It will then dispose of the resources. + /// + /// + /// The input task sequence. + /// The resulting task sequence. + val toSeq: source: taskSeq<'T> -> seq<'T> + + /// Unwraps the taskSeq as a Task>. This function is non-blocking. + val toArrayAsync: source: taskSeq<'T> -> Task<'T[]> + + /// Unwraps the taskSeq as a Task>. This function is non-blocking. + val toListAsync: source: taskSeq<'T> -> Task<'T list> + + /// Unwraps the taskSeq as a Task>. This function is non-blocking. + val toResizeArrayAsync: source: taskSeq<'T> -> Task> + + /// Unwraps the taskSeq as a Task>. This function is non-blocking. + val toIListAsync: source: taskSeq<'T> -> Task> + + /// Create a taskSeq of an array. + val ofArray: source: 'T[] -> taskSeq<'T> + + /// Create a taskSeq of a list. + val ofList: source: 'T list -> taskSeq<'T> + + /// Create a taskSeq of a seq. + val ofSeq: source: seq<'T> -> taskSeq<'T> + + /// Create a taskSeq of a ResizeArray, aka List. + val ofResizeArray: source: ResizeArray<'T> -> taskSeq<'T> + + /// Create a taskSeq of a sequence of tasks, that may already have hot-started. + val ofTaskSeq: source: seq<#Task<'T>> -> taskSeq<'T> + + /// Create a taskSeq of a list of tasks, that may already have hot-started. + val ofTaskList: source: #Task<'T> list -> taskSeq<'T> + + /// Create a taskSeq of an array of tasks, that may already have hot-started. + val ofTaskArray: source: #Task<'T> array -> taskSeq<'T> + + /// Create a taskSeq of a seq of async. + val ofAsyncSeq: source: seq> -> taskSeq<'T> + + /// Create a taskSeq of a list of async. + val ofAsyncList: source: Async<'T> list -> taskSeq<'T> + + /// Create a taskSeq of an array of async. + val ofAsyncArray: source: Async<'T> array -> taskSeq<'T> + + /// + /// Boxes as type each item in the sequence asynchyronously. + /// + val box: source: taskSeq<'T> -> taskSeq + + /// + /// Unboxes to the target type each item in the sequence asynchyronously. + /// The target type must be a or a built-in value type. + /// + /// Thrown when the function is unable to cast an item to the target type. + val unbox<'U when 'U: struct> : source: taskSeq -> taskSeq<'U> + + /// + /// Casts each item in the untyped sequence asynchyronously. If your types are boxed struct types + /// it is recommended to use instead. + /// + /// Thrown when the function is unable to cast an item to the target type. + val cast: source: taskSeq -> taskSeq<'T> + + /// Iterates over the taskSeq applying the action function to each item. This function is non-blocking + /// exhausts the sequence as soon as the task is evaluated. + val iter: action: ('T -> unit) -> source: taskSeq<'T> -> Task + + /// Iterates over the taskSeq applying the action function to each item. This function is non-blocking, + /// exhausts the sequence as soon as the task is evaluated. + val iteri: action: (int -> 'T -> unit) -> source: taskSeq<'T> -> Task + + /// Iterates over the taskSeq applying the async action to each item. This function is non-blocking + /// exhausts the sequence as soon as the task is evaluated. + val iterAsync: action: ('T -> #Task) -> source: taskSeq<'T> -> Task + + /// Iterates over the taskSeq, applying the async action to each item. This function is non-blocking, + /// exhausts the sequence as soon as the task is evaluated. + val iteriAsync: action: (int -> 'T -> #Task) -> source: taskSeq<'T> -> Task + + /// Maps over the taskSeq, applying the mapper function to each item. This function is non-blocking. + val map: mapper: ('T -> 'U) -> source: taskSeq<'T> -> taskSeq<'U> + + /// + /// Builds a new task sequence whose elements are the corresponding elements of the input task + /// sequence paired with the integer index (from 0) of each element. + /// Does not evaluate the input sequence until requested. + /// + /// The input task sequence. + /// The resulting task sequence of tuples. + /// Thrown when the input sequence is null. + val indexed: source: taskSeq<'T> -> taskSeq + + /// Maps over the taskSeq with an index, applying the mapper function to each item. This function is non-blocking. + val mapi: mapper: (int -> 'T -> 'U) -> source: taskSeq<'T> -> taskSeq<'U> + + /// Maps over the taskSeq, applying the async mapper function to each item. This function is non-blocking. + val mapAsync: mapper: ('T -> #Task<'U>) -> source: taskSeq<'T> -> taskSeq<'U> + + /// Maps over the taskSeq with an index, applying the async mapper function to each item. This function is non-blocking. + val mapiAsync: mapper: (int -> 'T -> #Task<'U>) -> source: taskSeq<'T> -> taskSeq<'U> + + /// Applies the given function to the items in the taskSeq and concatenates all the results in order. + val collect: binder: ('T -> #taskSeq<'U>) -> source: taskSeq<'T> -> taskSeq<'U> + + /// Applies the given function to the items in the taskSeq and concatenates all the results in order. + val collectSeq: binder: ('T -> #seq<'U>) -> source: taskSeq<'T> -> taskSeq<'U> + + /// Applies the given async function to the items in the taskSeq and concatenates all the results in order. + val collectAsync: binder: ('T -> #Task<'TSeqU>) -> source: taskSeq<'T> -> taskSeq<'U> when 'TSeqU :> taskSeq<'U> + + /// Applies the given async function to the items in the taskSeq and concatenates all the results in order. + val collectSeqAsync: binder: ('T -> #Task<'SeqU>) -> source: taskSeq<'T> -> taskSeq<'U> when 'SeqU :> seq<'U> + + /// + /// Returns the first element of the task sequence from , or if the sequence is empty. + /// + val tryHead: source: taskSeq<'T> -> Task<'T option> + + /// + /// Returns the first elementof the task sequence from + /// + /// Thrown when the sequence is empty. + val head: source: taskSeq<'T> -> Task<'T> + + /// + /// Returns the whole task sequence from , minus its first element, or if the sequence is empty. + /// + val tryTail: source: taskSeq<'T> -> Task option> + + /// + /// Returns the whole task sequence from , minus its first element. + /// + /// Thrown when the sequence is empty. + val tail: source: taskSeq<'T> -> Task> + + /// + /// Returns the last element of the task sequence from , or if the sequence is empty. + /// + val tryLast: source: taskSeq<'T> -> Task<'T option> + + /// + /// Returns the last element of the . + /// + /// Thrown when the sequence is empty. + val last: source: taskSeq<'T> -> Task<'T> + + /// + /// Returns the nth element of the , or if the sequence + /// does not contain enough elements, or if is negative. + /// Parameter is zero-based, that is, the value 0 returns the first element. + /// + val tryItem: index: int -> source: taskSeq<'T> -> Task<'T option> + + /// + /// Returns the nth element of the , or if the sequence + /// does not contain enough elements, or if is negative. + /// + /// Thrown when the sequence has insufficient length or + /// is negative. + val item: index: int -> source: taskSeq<'T> -> Task<'T> + + /// + /// Returns the only element of the task sequence, or if the sequence is empty of + /// contains more than one element. + /// + val tryExactlyOne: source: taskSeq<'T> -> Task<'T option> + + /// + /// Returns the only element of the task sequence. + /// + /// Thrown when the input sequence does not contain precisely one element. + val exactlyOne: source: taskSeq<'T> -> Task<'T> + + /// + /// Applies the given function to each element of the task sequence. Returns + /// a sequence comprised of the results "x" for each element where + /// the function returns Some(x). + /// If is asynchronous, consider using . + /// + val choose: chooser: ('T -> 'U option) -> source: taskSeq<'T> -> taskSeq<'U> + + /// + /// Applies the given asynchronous function to each element of the task sequence. Returns + /// a sequence comprised of the results "x" for each element where + /// the function returns . + /// If does not need to be asynchronous, consider using . + /// + val chooseAsync: chooser: ('T -> #Task<'U option>) -> source: taskSeq<'T> -> taskSeq<'U> + + /// + /// Returns a new collection containing only the elements of the collection + /// for which the given function returns . + /// If is asynchronous, consider using . + /// + val filter: predicate: ('T -> bool) -> source: taskSeq<'T> -> taskSeq<'T> + + /// + /// Returns a new collection containing only the elements of the collection + /// for which the given asynchronous function returns . + /// If does not need to be asynchronous, consider using . + /// + val filterAsync: predicate: ('T -> #Task) -> source: taskSeq<'T> -> taskSeq<'T> + + /// + /// Applies the given function to successive elements of the task sequence + /// in , returning the first result where the function returns . + /// If is asynchronous, consider using . + /// + val tryPick: chooser: ('T -> 'U option) -> source: taskSeq<'T> -> Task<'U option> + + /// + /// Applies the given asynchronous function to successive elements of the task sequence + /// in , returning the first result where the function returns . + /// If does not need to be asynchronous, consider using . + /// + val tryPickAsync: chooser: ('T -> #Task<'U option>) -> source: taskSeq<'T> -> Task<'U option> + + /// + /// Returns the first element of the task sequence in for which the given function + /// returns . Returns if no such element exists. + /// If is asynchronous, consider using . + /// + val tryFind: predicate: ('T -> bool) -> source: taskSeq<'T> -> Task<'T option> + + /// + /// Returns the first element of the task sequence in for which the given asynchronous function + /// returns . Returns if no such element exists. + /// If does not need to be asynchronous, consider using . + /// + val tryFindAsync: predicate: ('T -> #Task) -> source: taskSeq<'T> -> Task<'T option> + + /// + /// Returns the index, starting from zero, of the task sequence in for which the given function + /// returns . Returns if no such element exists. + /// If is asynchronous, consider using . + /// + val tryFindIndex: predicate: ('T -> bool) -> source: taskSeq<'T> -> Task + + /// + /// Returns the index, starting from zero, of the task sequence in for which the given asynchronous function + /// returns . Returns if no such element exists. + /// If does not need to be asynchronous, consider using . + /// + val tryFindIndexAsync: predicate: ('T -> #Task) -> source: taskSeq<'T> -> Task + + + /// + /// Applies the given function to successive elements of the task sequence + /// in , returning the first result where the function returns . + /// If is asynchronous, consider using . + /// Thrown when every item of the sequence + /// evaluates to when the given function is applied. + /// + val pick: chooser: ('T -> 'U option) -> source: taskSeq<'T> -> Task<'U> + + /// + /// Applies the given asynchronous function to successive elements of the task sequence + /// in , returning the first result where the function returns . + /// If does not need to be asynchronous, consider using . + /// Thrown when every item of the sequence + /// evaluates to when the given function is applied. + /// + val pickAsync: chooser: ('T -> #Task<'U option>) -> source: taskSeq<'T> -> Task<'U> + + /// + /// Returns the first element of the task sequence in for which the given function + /// returns . + /// If is asynchronous, consider using . + /// + /// Thrown if no element returns when + /// evaluated by the function. + val find: predicate: ('T -> bool) -> source: taskSeq<'T> -> Task<'T> + + /// + /// Returns the first element of the task sequence in for which the given + /// asynchronous function returns . + /// If does not need to be asynchronous, consider using . + /// + /// Thrown if no element returns when + /// evaluated by the function. + val findAsync: predicate: ('T -> #Task) -> source: taskSeq<'T> -> Task<'T> + + /// + /// Returns the index, starting from zero, of the first element of the task sequence in for which + /// the given function returns . + /// If is asynchronous, consider using . + /// + /// Thrown if no element returns when + /// evaluated by the function. + val findIndex: predicate: ('T -> bool) -> source: taskSeq<'T> -> Task + + /// + /// Returns the index, starting from zero, of the task sequence in for which the given + /// asynchronous function returns . + /// If does not need to be asynchronous, consider using . + /// + /// + /// Thrown if no element returns when + /// evaluated by the function. + val findIndexAsync: predicate: ('T -> #Task) -> source: taskSeq<'T> -> Task + + /// + /// Tests if the sequence contains the specified element. Returns + /// if contains the specified element; + /// otherwise. + /// + /// + /// The value to locate in the input sequence. + /// The input sequence. + /// True if the input sequence contains the specified element; false otherwise. + /// Thrown when the input sequence is null. + val contains<'T when 'T: equality> : value: 'T -> source: taskSeq<'T> -> Task + + /// + /// Tests if any element of the task sequence in satisfies + /// the given . + /// The function is applied to the elements of the input sequence. If any application + /// returns then the overall result is and no further elements are evaluated and tested. + /// Otherwise, is returned. + /// + /// + /// A function to test each item of the input sequence. + /// The input sequence. /// + /// True if any result from the predicate is true; false otherwise. /// + /// Thrown when the input sequence is null. + val exists: predicate: ('T -> bool) -> source: taskSeq<'T> -> Task + + /// + /// Tests if any element of the task sequence in satisfies + /// the given async . + /// The function is applied to the elements of the input sequence. If any application + /// returns then the overall result is and no further elements are evaluated and tested. + /// Otherwise, is returned. + /// + /// + /// A function to test each item of the input sequence. + /// The input sequence. /// + /// True if any result from the predicate is true; false otherwise. /// + /// Thrown when the input sequence is null. + val existsAsync: predicate: ('T -> #Task) -> source: taskSeq<'T> -> Task + + /// + /// Returns a new task sequence with the distinct elements of the second task sequence which do not appear in the + /// , using generic hash and equality comparisons to compare values. + /// + /// + /// + /// Note that this function returns a task sequence that digests the whole of the first input task sequence as soon as + /// the result sequence first gets awaited or iterated. As a result this function should not be used with + /// large or infinite sequences in the first parameter. The function makes no assumption on the ordering of the first input + /// sequence. + /// + /// + /// A task sequence whose elements that also occur in the second sequence will cause those elements to be removed from the returned sequence. + /// A sequence whose elements that are not also in first will be returned. + /// A sequence that contains the set difference of the elements of two sequences. + /// + /// Thrown when either of the two input sequences is null. + val except<'T when 'T: equality> : itemsToExclude: taskSeq<'T> -> source: taskSeq<'T> -> taskSeq<'T> + + /// + /// Returns a new task sequence with the distinct elements of the second task sequence which do not appear in the + /// , using generic hash and equality comparisons to compare values. + /// + /// + /// + /// Note that this function returns a task sequence that digests the whole of the first input task sequence as soon as + /// the result sequence first gets awaited or iterated. As a result this function should not be used with + /// large or infinite sequences in the first parameter. The function makes no assumption on the ordering of the first input + /// sequence. + /// + /// + /// A task sequence whose elements that also occur in the second sequence will cause those elements to be removed from the returned sequence. + /// A sequence whose elements that are not also in first will be returned. + /// A sequence that contains the set difference of the elements of two sequences. + /// + /// Thrown when either of the two input sequences is null. + val exceptOfSeq<'T when 'T: equality> : itemsToExclude: seq<'T> -> source: taskSeq<'T> -> taskSeq<'T> + + /// + /// Zips two task sequences, returning a taskSeq of the tuples of each sequence, in order. May raise ArgumentException + /// if the sequences are or unequal length. + /// + /// The sequences have different lengths. + val zip: source1: taskSeq<'T> -> source2: taskSeq<'U> -> taskSeq<'T * 'U> + + /// + /// Applies the function to each element in the task sequence, + /// threading an accumulator argument of type through the computation. + /// If the accumulator function is asynchronous, consider using . + /// + val fold: folder: ('State -> 'T -> 'State) -> state: 'State -> source: taskSeq<'T> -> Task<'State> + + /// + /// Applies the asynchronous function to each element in the task sequence, + /// threading an accumulator argument of type through the computation. + /// If the accumulator function does not need to be asynchronous, consider using . + /// + val foldAsync: folder: ('State -> 'T -> #Task<'State>) -> state: 'State -> source: taskSeq<'T> -> Task<'State> From fa8962b3b282e4f32d4ba05821f72a41c9b552e5 Mon Sep 17 00:00:00 2001 From: Abel Braaksma Date: Mon, 28 Nov 2022 03:55:22 +0100 Subject: [PATCH 5/5] Update release-notes for PR #111 --- release-notes.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/release-notes.txt b/release-notes.txt index 8b5c0802..1b5a945f 100644 --- a/release-notes.txt +++ b/release-notes.txt @@ -2,6 +2,7 @@ Release notes: 0.3.0 (unreleased) + - internal renames, improved doc comments, signature files for complex types, hide internal-only types, #111. - adds support for static TaskLike, allowing the same let! and do! overloads that F# task supports, fixes #110. - implements 'do!' for non-generic Task like with Task.Delay, fixes #43. - adds support for 'for .. in ..' with task sequences in F# tasks and async, #75, #93 and #99 (with help from @theangrybyrd).