From 5cb8ea8a14bf3ca241aacbb5a1edc7c58fc47bd5 Mon Sep 17 00:00:00 2001 From: Abel Braaksma Date: Fri, 22 Dec 2023 01:20:29 +0100 Subject: [PATCH 01/19] Better wording for `takeWhile`-like functions --- src/FSharp.Control.TaskSeq/TaskSeq.fsi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/FSharp.Control.TaskSeq/TaskSeq.fsi b/src/FSharp.Control.TaskSeq/TaskSeq.fsi index c34895d2..de8b8120 100644 --- a/src/FSharp.Control.TaskSeq/TaskSeq.fsi +++ b/src/FSharp.Control.TaskSeq/TaskSeq.fsi @@ -818,7 +818,7 @@ type TaskSeq = /// /// Returns a task sequence that, when iterated, yields elements of the underlying sequence while the /// given function returns , and then returns no further elements. - /// The first element where the predicate returns is not included in the resulting sequence + /// Stops consuming the source and yielding items as soon as the predicate returns false. /// (see also ). /// If is asynchronous, consider using . /// @@ -832,7 +832,7 @@ type TaskSeq = /// /// Returns a task sequence that, when iterated, yields elements of the underlying sequence while the /// given asynchronous function returns , and then returns no further elements. - /// The first element where the predicate returns is not included in the resulting sequence + /// Stops consuming the source and yielding items as soon as the predicate returns false. /// (see also ). /// If is synchronous, consider using . /// From 4245c623b6dda867247a2daf2421c2bfe0c3f566 Mon Sep 17 00:00:00 2001 From: Ruben Bartelink Date: Fri, 22 Dec 2023 12:10:23 +0000 Subject: [PATCH 02/19] Unmangle xmldoc --- src/FSharp.Control.TaskSeq/TaskSeq.fsi | 32 ++++++++++++++------------ 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/FSharp.Control.TaskSeq/TaskSeq.fsi b/src/FSharp.Control.TaskSeq/TaskSeq.fsi index de8b8120..5cf6ca6b 100644 --- a/src/FSharp.Control.TaskSeq/TaskSeq.fsi +++ b/src/FSharp.Control.TaskSeq/TaskSeq.fsi @@ -81,7 +81,7 @@ type TaskSeq = /// /// Generates a new task sequence which, when iterated, will return successive elements by calling the given function - /// with the curren zero-basedt index, up to the given count. Each element is saved after its initialization for successive access to + /// with the current zero-based 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 @@ -454,58 +454,58 @@ type TaskSeq = static member indexed: source: TaskSeq<'T> -> TaskSeq /// - /// Builds a new task sequence whose elements are the results of applying the + /// Builds a new task sequence whose elements are the results of applying the /// function to each of the elements of the input task sequence in . /// The given function will be applied as elements are pulled using the /// method on async enumerators retrieved from the input task sequence. /// Does not evaluate the input sequence until requested. /// /// - /// A function to transform items from the input task sequence. + /// A function to transform items from the input task sequence. /// The input task sequence. /// The resulting task sequence. /// Thrown when the input task sequence is null. static member map: mapper: ('T -> 'U) -> source: TaskSeq<'T> -> TaskSeq<'U> /// - /// Builds a new task sequence whose elements are the results of applying the + /// Builds a new task sequence whose elements are the results of applying the /// function to each of the elements of the input task sequence in , passing - /// an extra zero-based index argument to the function. + /// an extra zero-based index argument to the function. /// The given function will be applied as elements are pulled using the /// method on async enumerators retrieved from the input task sequence. /// Does not evaluate the input sequence until requested. /// /// - /// A function to transform items from the input task sequence that also access the current index. + /// A function to transform items from the input task sequence that also access the current index. /// The input task sequence. /// The resulting task sequence. /// Thrown when the input task sequence is null. static member mapi: mapper: (int -> 'T -> 'U) -> source: TaskSeq<'T> -> TaskSeq<'U> /// - /// Builds a new task sequence whose elements are the results of applying the asynchronous + /// Builds a new task sequence whose elements are the results of applying the asynchronous /// function to each of the elements of the input task sequence in . /// The given function will be applied as elements are pulled using the /// method on async enumerators retrieved from the input task sequence. /// Does not evaluate the input sequence until requested. /// /// - /// An asynchronous function to transform items from the input task sequence. + /// An asynchronous function to transform items from the input task sequence. /// The input task sequence. /// The resulting task sequence. /// Thrown when the input task sequence is null. static member mapAsync: mapper: ('T -> #Task<'U>) -> source: TaskSeq<'T> -> TaskSeq<'U> /// - /// Builds a new task sequence whose elements are the results of applying the asynchronous + /// Builds a new task sequence whose elements are the results of applying the asynchronous /// function to each of the elements of the input task sequence in , passing - /// an extra zero-based index argument to the function. + /// an extra zero-based index argument to the function. /// The given function will be applied as elements are pulled using the /// method on async enumerators retrieved from the input task sequence. /// Does not evaluate the input sequence until requested. /// /// - /// An asynchronous function to transform items from the input task sequence that also access the current index. + /// An asynchronous function to transform items from the input task sequence that also access the current index. /// The input task sequence. /// The resulting task sequence. /// Thrown when the input task sequence is null. @@ -640,6 +640,7 @@ type TaskSeq = /// /// /// The input task sequence. + /// The index of the item to retrieve. /// The nth element of the task sequence, or None if it doesn't exist. /// Thrown when the input task sequence is null. static member tryItem: index: int -> source: TaskSeq<'T> -> Task<'T option> @@ -651,6 +652,7 @@ type TaskSeq = /// /// /// The input task sequence. + /// The index of the item to retrieve. /// The nth element of the task sequence. /// Thrown when the input task sequence is null. /// Thrown when the sequence has insufficient length or is negative. @@ -1163,8 +1165,8 @@ type TaskSeq = /// /// Applies the function to each element in the task sequence, threading an accumulator - /// argument of type through the computation. If the input function is and the elements are - /// then computes . + /// argument of type through the computation. If the input function is f and the elements are i0...iN /> + /// then computes f (... (f s i0)...) iN />. /// If the accumulator function is asynchronous, consider using . /// /// @@ -1177,8 +1179,8 @@ type TaskSeq = /// /// Applies the asynchronous function to each element in the task sequence, threading an accumulator - /// argument of type through the computation. If the input function is and the elements are - /// then computes . + /// argument of type through the computation. If the input function is f /> and the elements are i0...iN /> + /// then computes f (... (f s i0)...) iN />. /// If the accumulator function is synchronous, consider using . /// /// From ef4d2cf41214060c043c8fea3f545026d864ad03 Mon Sep 17 00:00:00 2001 From: Ruben Bartelink Date: Fri, 22 Dec 2023 12:19:38 +0000 Subject: [PATCH 03/19] Add Dictionary --- src/FSharp.Control.TaskSeq.sln.DotSettings | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 src/FSharp.Control.TaskSeq.sln.DotSettings diff --git a/src/FSharp.Control.TaskSeq.sln.DotSettings b/src/FSharp.Control.TaskSeq.sln.DotSettings new file mode 100644 index 00000000..7e8b4870 --- /dev/null +++ b/src/FSharp.Control.TaskSeq.sln.DotSettings @@ -0,0 +1,2 @@ + + True \ No newline at end of file From d6660e91fe42e19296aa818e5f042e1ff196a7de Mon Sep 17 00:00:00 2001 From: Ruben Bartelink Date: Fri, 22 Dec 2023 12:36:18 +0000 Subject: [PATCH 04/19] Tweak skipWhile* xmldoc --- src/FSharp.Control.TaskSeq/TaskSeq.fsi | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/FSharp.Control.TaskSeq/TaskSeq.fsi b/src/FSharp.Control.TaskSeq/TaskSeq.fsi index 5cf6ca6b..c7d9442b 100644 --- a/src/FSharp.Control.TaskSeq/TaskSeq.fsi +++ b/src/FSharp.Control.TaskSeq/TaskSeq.fsi @@ -876,8 +876,8 @@ type TaskSeq = /// /// Returns a task sequence that, when iterated, skips elements of the underlying sequence while the /// given function returns , and then yields the remaining - /// elements. The first element where the predicate returns is returned, which means that this - /// function will skip 0 or more elements (see also ). + /// elements. Elements where the predicate returns are propagated, which means that this + /// function may not skip any elements (see also ). /// If is asynchronous, consider using . /// /// @@ -890,8 +890,8 @@ type TaskSeq = /// /// Returns a task sequence that, when iterated, skips elements of the underlying sequence while the /// given asynchronous function returns , and then yields the - /// remaining elements. The first element where the predicate returns is returned, which - /// means that this function will skip 0 or more elements (see also ). + /// remaining elements. Elements where the predicate returns are propagated, which means that this + /// function may not skip any elements (see also ). /// If is synchronous, consider using . /// /// @@ -903,13 +903,13 @@ type TaskSeq = /// /// Returns a task sequence that, when iterated, skips elements of the underlying sequence until the given - /// function returns , also skips that element - /// and then yields the remaining elements (see also ). This function skips + /// function returns , also skips that element + /// and then yields the remaining elements (see also ). It will thus always skip /// at least one element of a non-empty sequence, or returns the empty task sequence if the input is empty. /// If is asynchronous, consider using . /// ` /// - /// A function that evaluates to false when no more items should be skipped. + /// A function that evaluates to false for the final item to be skipped. /// The input task sequence. /// The resulting task sequence. /// Thrown when the input task sequence is null. @@ -917,13 +917,13 @@ type TaskSeq = /// /// Returns a task sequence that, when iterated, skips elements of the underlying sequence until the given - /// function returns , also skips that element - /// and then yields the remaining elements (see also ). This function skips + /// function returns , also skips that element + /// and then yields the remaining elements (see also ). It will thus always skip /// at least one element of a non-empty sequence, or returns the empty task sequence if the input is empty. /// If is synchronous, consider using . /// /// - /// An asynchronous function that evaluates to false when no more items should be skipped. + /// An asynchronous function that evaluates to false for the final item to be skipped. /// The input task sequence. /// The resulting task sequence. /// Thrown when the input task sequence is null. From 787ba7309213e2f5b60e5de778cc413dcdb919a1 Mon Sep 17 00:00:00 2001 From: Ruben Bartelink Date: Fri, 22 Dec 2023 12:38:03 +0000 Subject: [PATCH 05/19] Formatting / remove redundant parens --- src/FSharp.Control.TaskSeq/TaskSeq.fs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/FSharp.Control.TaskSeq/TaskSeq.fs b/src/FSharp.Control.TaskSeq/TaskSeq.fs index d3aedcb9..d1819e31 100644 --- a/src/FSharp.Control.TaskSeq/TaskSeq.fs +++ b/src/FSharp.Control.TaskSeq/TaskSeq.fs @@ -314,15 +314,15 @@ type TaskSeq private () = static member exists predicate source = Internal.tryFind (Predicate predicate) source - |> Task.map (Option.isSome) + |> Task.map Option.isSome static member existsAsync predicate source = Internal.tryFind (PredicateAsync predicate) source - |> Task.map (Option.isSome) + |> Task.map Option.isSome static member contains value source = Internal.tryFind (Predicate((=) value)) source - |> Task.map (Option.isSome) + |> Task.map Option.isSome static member pick chooser source = Internal.tryPick (TryPick chooser) source @@ -348,8 +348,6 @@ type TaskSeq private () = Internal.tryFindIndex (PredicateAsync predicate) source |> Task.map (Option.defaultWith Internal.raiseNotFound) - - // // zip/unzip/fold etc functions // From cfc96240233e5a9cc4822e6a43f342102c64d629 Mon Sep 17 00:00:00 2001 From: Ruben Bartelink Date: Fri, 22 Dec 2023 12:38:59 +0000 Subject: [PATCH 06/19] Fix code inspections --- src/FSharp.Control.TaskSeq/TaskSeqInternal.fs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs b/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs index e59d50c3..f50eb155 100644 --- a/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs +++ b/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs @@ -86,7 +86,7 @@ module internal TaskSeqInternal = let empty<'T> = { new IAsyncEnumerable<'T> with - member _.GetAsyncEnumerator(_) = + member _.GetAsyncEnumerator _ = { new IAsyncEnumerator<'T> with member _.MoveNextAsync() = ValueTask.False member _.Current = Unchecked.defaultof<'T> @@ -96,7 +96,7 @@ module internal TaskSeqInternal = let singleton (value: 'T) = { new IAsyncEnumerable<'T> with - member _.GetAsyncEnumerator(_) = + member _.GetAsyncEnumerator _ = let mutable status = BeforeAll { new IAsyncEnumerator<'T> with @@ -927,7 +927,7 @@ module internal TaskSeqInternal = if step then // only create hashset by the time we actually start iterating - use hashSet = new ConcurrentHashSet<_>(CancellationToken.None) + use hashSet = ConcurrentHashSet<_>(CancellationToken.None) do! hashSet.AddManyAsync itemsToExclude while go do @@ -955,7 +955,7 @@ module internal TaskSeqInternal = if step then // only create hashset by the time we actually start iterating - use hashSet = new ConcurrentHashSet<_>(CancellationToken.None) + use hashSet = ConcurrentHashSet<_>(CancellationToken.None) do hashSet.AddMany itemsToExclude while go do From c6f24ada48b319321f02b932b9cf7e5b7c351ef8 Mon Sep 17 00:00:00 2001 From: Ruben Bartelink Date: Fri, 22 Dec 2023 12:39:46 +0000 Subject: [PATCH 07/19] More dictionary entries --- src/FSharp.Control.TaskSeq.sln.DotSettings | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/FSharp.Control.TaskSeq.sln.DotSettings b/src/FSharp.Control.TaskSeq.sln.DotSettings index 7e8b4870..a8d97860 100644 --- a/src/FSharp.Control.TaskSeq.sln.DotSettings +++ b/src/FSharp.Control.TaskSeq.sln.DotSettings @@ -1,2 +1,5 @@  - True \ No newline at end of file + True + True + True + True \ No newline at end of file From dedbdb32d0f9600cf25751a566dacf41fed7c780 Mon Sep 17 00:00:00 2001 From: Ruben Bartelink Date: Fri, 22 Dec 2023 14:44:23 +0000 Subject: [PATCH 08/19] Tidy/comment impl --- src/FSharp.Control.TaskSeq/TaskSeqInternal.fs | 106 +++++++----------- 1 file changed, 41 insertions(+), 65 deletions(-) diff --git a/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs b/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs index f50eb155..33682a63 100644 --- a/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs +++ b/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs @@ -789,77 +789,53 @@ module internal TaskSeqInternal = taskSeq { use e = source.GetAsyncEnumerator CancellationToken.None - let! moveFirst = e.MoveNextAsync() - let mutable more = moveFirst + match! e.MoveNextAsync() with + | false -> () // Nothing further to do, no matter what the rules are + | true -> + + let mutable cont = true match whileKind, predicate with | Exclusive, Predicate predicate -> // skipWhile - while more && predicate e.Current do - let! hasMore = e.MoveNextAsync() - more <- hasMore - - if more then - // yield the last one where the predicate was false - // (this ensures we skip 0 or more) - yield e.Current - - while! e.MoveNextAsync() do // get the rest - yield e.Current - + while cont do + if predicate e.Current then // spam -> skip + let! hasAnother = e.MoveNextAsync() + cont <- hasAnother + else // Starting the ham; we shouldn't skip this one + yield e.Current // So we return the item as it does not meet the condition for skipping + while! e.MoveNextAsync() do // propagate the rest + yield e.Current + cont <- false | Inclusive, Predicate predicate -> // skipWhileInclusive - while more && predicate e.Current do - let! hasMore = e.MoveNextAsync() - more <- hasMore - - if more then - // yield the rest (this ensures we skip 1 or more) - while! e.MoveNextAsync() do - yield e.Current - + while cont do + if predicate e.Current then // spam -> skip + let! hasAnother = e.MoveNextAsync() + cont <- hasAnother + else // Starting the ham, but _Inclusive_ means we skip yielding the first one that failed the predicate + while! e.MoveNextAsync() do // propagate the rest + yield e.Current + cont <- false | Exclusive, PredicateAsync predicate -> // skipWhileAsync - let mutable cont = true - - if more then - let! hasMore = predicate e.Current - cont <- hasMore - - while more && cont do - let! moveNext = e.MoveNextAsync() - - if moveNext then - let! hasMore = predicate e.Current - cont <- hasMore - - more <- moveNext - - if more then - // yield the last one where the predicate was false - // (this ensures we skip 0 or more) - yield e.Current - - while! e.MoveNextAsync() do // get the rest - yield e.Current - + while cont do + let! shouldSkipIt = predicate e.Current + if shouldSkipIt then + let! hasAnother = e.MoveNextAsync() + cont <- hasAnother + else // We're starting the ham + yield e.Current // Yield the one that just failed the skip test + while! e.MoveNextAsync() do // propagate the rest + yield e.Current + cont <- false | Inclusive, PredicateAsync predicate -> // skipWhileInclusiveAsync - let mutable cont = true - - if more then - let! hasMore = predicate e.Current - cont <- hasMore - - while more && cont do - let! moveNext = e.MoveNextAsync() - - if moveNext then - let! hasMore = predicate e.Current - cont <- hasMore - - more <- moveNext - - if more then - // get the rest, this gives 1 or more semantics - while! e.MoveNextAsync() do - yield e.Current + while cont do + let! shouldSkipIt = predicate e.Current + if shouldSkipIt then + let! gotOne = e.MoveNextAsync() + cont <- gotOne + else // Starting the ham, but _Inclusive_ means we skip yielding the first one that failed the predicate + while! e.MoveNextAsync() do // propagate the rest + yield e.Current + cont <- false } // Consider turning using an F# version of this instead? From 7f470cf9ec3ba1881611c03eae485e3776b883cd Mon Sep 17 00:00:00 2001 From: Ruben Bartelink Date: Fri, 22 Dec 2023 14:54:26 +0000 Subject: [PATCH 09/19] Revert omitting `new` from IAsyncDisposable --- src/FSharp.Control.TaskSeq/TaskSeqInternal.fs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs b/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs index 33682a63..34f990d6 100644 --- a/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs +++ b/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs @@ -903,7 +903,7 @@ module internal TaskSeqInternal = if step then // only create hashset by the time we actually start iterating - use hashSet = ConcurrentHashSet<_>(CancellationToken.None) + use hashSet = new ConcurrentHashSet<_>(CancellationToken.None) do! hashSet.AddManyAsync itemsToExclude while go do @@ -931,7 +931,7 @@ module internal TaskSeqInternal = if step then // only create hashset by the time we actually start iterating - use hashSet = ConcurrentHashSet<_>(CancellationToken.None) + use hashSet = new ConcurrentHashSet<_>(CancellationToken.None) do hashSet.AddMany itemsToExclude while go do From 5a25eccd89c845fc14ebc5b1906b4fc8ce1bc5c7 Mon Sep 17 00:00:00 2001 From: Ruben Bartelink Date: Fri, 22 Dec 2023 14:58:23 +0000 Subject: [PATCH 10/19] Fantomas --- src/FSharp.Control.TaskSeq/TaskSeqInternal.fs | 93 ++++++++++--------- 1 file changed, 51 insertions(+), 42 deletions(-) diff --git a/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs b/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs index 34f990d6..21364521 100644 --- a/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs +++ b/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs @@ -789,53 +789,62 @@ module internal TaskSeqInternal = taskSeq { use e = source.GetAsyncEnumerator CancellationToken.None + match! e.MoveNextAsync() with | false -> () // Nothing further to do, no matter what the rules are | true -> - let mutable cont = true + let mutable cont = true - match whileKind, predicate with - | Exclusive, Predicate predicate -> // skipWhile - while cont do - if predicate e.Current then // spam -> skip - let! hasAnother = e.MoveNextAsync() - cont <- hasAnother - else // Starting the ham; we shouldn't skip this one - yield e.Current // So we return the item as it does not meet the condition for skipping - while! e.MoveNextAsync() do // propagate the rest - yield e.Current - cont <- false - | Inclusive, Predicate predicate -> // skipWhileInclusive - while cont do - if predicate e.Current then // spam -> skip - let! hasAnother = e.MoveNextAsync() - cont <- hasAnother - else // Starting the ham, but _Inclusive_ means we skip yielding the first one that failed the predicate - while! e.MoveNextAsync() do // propagate the rest - yield e.Current - cont <- false - | Exclusive, PredicateAsync predicate -> // skipWhileAsync - while cont do - let! shouldSkipIt = predicate e.Current - if shouldSkipIt then - let! hasAnother = e.MoveNextAsync() - cont <- hasAnother - else // We're starting the ham - yield e.Current // Yield the one that just failed the skip test - while! e.MoveNextAsync() do // propagate the rest - yield e.Current - cont <- false - | Inclusive, PredicateAsync predicate -> // skipWhileInclusiveAsync - while cont do - let! shouldSkipIt = predicate e.Current - if shouldSkipIt then - let! gotOne = e.MoveNextAsync() - cont <- gotOne - else // Starting the ham, but _Inclusive_ means we skip yielding the first one that failed the predicate - while! e.MoveNextAsync() do // propagate the rest - yield e.Current - cont <- false + match whileKind, predicate with + | Exclusive, Predicate predicate -> // skipWhile + while cont do + if predicate e.Current then // spam -> skip + let! hasAnother = e.MoveNextAsync() + cont <- hasAnother + else // Starting the ham; we shouldn't skip this one + yield e.Current // So we return the item as it does not meet the condition for skipping + + while! e.MoveNextAsync() do // propagate the rest + yield e.Current + + cont <- false + | Inclusive, Predicate predicate -> // skipWhileInclusive + while cont do + if predicate e.Current then // spam -> skip + let! hasAnother = e.MoveNextAsync() + cont <- hasAnother + else // Starting the ham, but _Inclusive_ means we skip yielding the first one that failed the predicate + while! e.MoveNextAsync() do // propagate the rest + yield e.Current + + cont <- false + | Exclusive, PredicateAsync predicate -> // skipWhileAsync + while cont do + let! shouldSkipIt = predicate e.Current + + if shouldSkipIt then + let! hasAnother = e.MoveNextAsync() + cont <- hasAnother + else // We're starting the ham + yield e.Current // Yield the one that just failed the skip test + + while! e.MoveNextAsync() do // propagate the rest + yield e.Current + + cont <- false + | Inclusive, PredicateAsync predicate -> // skipWhileInclusiveAsync + while cont do + let! shouldSkipIt = predicate e.Current + + if shouldSkipIt then + let! gotOne = e.MoveNextAsync() + cont <- gotOne + else // Starting the ham, but _Inclusive_ means we skip yielding the first one that failed the predicate + while! e.MoveNextAsync() do // propagate the rest + yield e.Current + + cont <- false } // Consider turning using an F# version of this instead? From 6d23990e936b885fa6fcb2ad430bff77597b20b3 Mon Sep 17 00:00:00 2001 From: Ruben Bartelink Date: Fri, 22 Dec 2023 15:03:29 +0000 Subject: [PATCH 11/19] Recoup 2 lines from Fantomas --- src/FSharp.Control.TaskSeq/TaskSeqInternal.fs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs b/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs index 21364521..15a2714e 100644 --- a/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs +++ b/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs @@ -821,12 +821,11 @@ module internal TaskSeqInternal = cont <- false | Exclusive, PredicateAsync predicate -> // skipWhileAsync while cont do - let! shouldSkipIt = predicate e.Current - - if shouldSkipIt then + match! predicate e.Current with + | true -> let! hasAnother = e.MoveNextAsync() cont <- hasAnother - else // We're starting the ham + | false -> // We're starting the ham yield e.Current // Yield the one that just failed the skip test while! e.MoveNextAsync() do // propagate the rest @@ -835,12 +834,11 @@ module internal TaskSeqInternal = cont <- false | Inclusive, PredicateAsync predicate -> // skipWhileInclusiveAsync while cont do - let! shouldSkipIt = predicate e.Current - - if shouldSkipIt then + match! predicate e.Current with + | true -> let! gotOne = e.MoveNextAsync() cont <- gotOne - else // Starting the ham, but _Inclusive_ means we skip yielding the first one that failed the predicate + | false -> // Starting the ham, but _Inclusive_ means we skip yielding the first one that failed the predicate while! e.MoveNextAsync() do // propagate the rest yield e.Current From 0546e40d2a3825b7da91a7045437ec001a0a783e Mon Sep 17 00:00:00 2001 From: Ruben Bartelink Date: Fri, 22 Dec 2023 15:10:56 +0000 Subject: [PATCH 12/19] Normal IDisposable? --- src/FSharp.Control.TaskSeq/TaskSeqInternal.fs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs b/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs index 15a2714e..6fdb3037 100644 --- a/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs +++ b/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs @@ -891,13 +891,11 @@ module internal TaskSeqInternal = go <- step } - interface IAsyncDisposable with - override _.DisposeAsync() = - if not (isNull _rwLock) then + interface IDisposable with + override _.Dispose() = + if isNull _rwLock |> not then _rwLock.Dispose() - ValueTask.CompletedTask - let except itemsToExclude (source: TaskSeq<_>) = checkNonNull (nameof source) source checkNonNull (nameof itemsToExclude) itemsToExclude From 26f95bfef0b2717892329076e3cf22b84e53cecf Mon Sep 17 00:00:00 2001 From: Ruben Bartelink Date: Fri, 22 Dec 2023 15:19:59 +0000 Subject: [PATCH 13/19] Remove stray tag closers --- src/FSharp.Control.TaskSeq/TaskSeq.fsi | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/FSharp.Control.TaskSeq/TaskSeq.fsi b/src/FSharp.Control.TaskSeq/TaskSeq.fsi index c7d9442b..e452dd0c 100644 --- a/src/FSharp.Control.TaskSeq/TaskSeq.fsi +++ b/src/FSharp.Control.TaskSeq/TaskSeq.fsi @@ -1165,8 +1165,8 @@ type TaskSeq = /// /// Applies the function to each element in the task sequence, threading an accumulator - /// argument of type through the computation. If the input function is f and the elements are i0...iN /> - /// then computes f (... (f s i0)...) iN />. + /// argument of type through the computation. If the input function is f and the elements are i0...iN + /// then computes f (... (f s i0)...) iN. /// If the accumulator function is asynchronous, consider using . /// /// @@ -1179,8 +1179,8 @@ type TaskSeq = /// /// Applies the asynchronous function to each element in the task sequence, threading an accumulator - /// argument of type through the computation. If the input function is f /> and the elements are i0...iN /> - /// then computes f (... (f s i0)...) iN />. + /// argument of type through the computation. If the input function is f and the elements are i0...iN + /// then computes f (... (f s i0)...) iN. /// If the accumulator function is synchronous, consider using . /// /// From 23b596ca78e0e9e70afee65f699376bb4d7e1f19 Mon Sep 17 00:00:00 2001 From: Ruben Bartelink Date: Fri, 22 Dec 2023 17:20:42 +0000 Subject: [PATCH 14/19] Collapse --- src/FSharp.Control.TaskSeq/TaskSeqInternal.fs | 40 ++++++------------- 1 file changed, 13 insertions(+), 27 deletions(-) diff --git a/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs b/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs index 6fdb3037..acbf2537 100644 --- a/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs +++ b/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs @@ -796,49 +796,35 @@ module internal TaskSeqInternal = let mutable cont = true - match whileKind, predicate with - | Exclusive, Predicate predicate -> // skipWhile - while cont do - if predicate e.Current then // spam -> skip - let! hasAnother = e.MoveNextAsync() - cont <- hasAnother - else // Starting the ham; we shouldn't skip this one - yield e.Current // So we return the item as it does not meet the condition for skipping + let exclusive = + match whileKind with + | Exclusive -> true + | Inclusive -> false - while! e.MoveNextAsync() do // propagate the rest - yield e.Current - - cont <- false - | Inclusive, Predicate predicate -> // skipWhileInclusive + match predicate with + | Predicate predicate -> // skipWhile(Inclusive)? while cont do if predicate e.Current then // spam -> skip let! hasAnother = e.MoveNextAsync() cont <- hasAnother - else // Starting the ham, but _Inclusive_ means we skip yielding the first one that failed the predicate + else // Starting the ham + if exclusive then + yield e.Current // return the item as it does not meet the condition for skipping + while! e.MoveNextAsync() do // propagate the rest yield e.Current cont <- false - | Exclusive, PredicateAsync predicate -> // skipWhileAsync + | PredicateAsync predicate -> // skipWhileAsync while cont do match! predicate e.Current with | true -> let! hasAnother = e.MoveNextAsync() cont <- hasAnother | false -> // We're starting the ham - yield e.Current // Yield the one that just failed the skip test - - while! e.MoveNextAsync() do // propagate the rest - yield e.Current + if exclusive then + yield e.Current // Yield the one that just failed the skip test - cont <- false - | Inclusive, PredicateAsync predicate -> // skipWhileInclusiveAsync - while cont do - match! predicate e.Current with - | true -> - let! gotOne = e.MoveNextAsync() - cont <- gotOne - | false -> // Starting the ham, but _Inclusive_ means we skip yielding the first one that failed the predicate while! e.MoveNextAsync() do // propagate the rest yield e.Current From c24fdedd7fbe939e9e28aa7f9962a3044ab62d24 Mon Sep 17 00:00:00 2001 From: Ruben Bartelink Date: Fri, 22 Dec 2023 17:26:13 +0000 Subject: [PATCH 15/19] Please fantomas --- src/FSharp.Control.TaskSeq/TaskSeqInternal.fs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs b/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs index acbf2537..fdbaff73 100644 --- a/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs +++ b/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs @@ -796,11 +796,6 @@ module internal TaskSeqInternal = let mutable cont = true - let exclusive = - match whileKind with - | Exclusive -> true - | Inclusive -> false - match predicate with | Predicate predicate -> // skipWhile(Inclusive)? while cont do @@ -808,8 +803,9 @@ module internal TaskSeqInternal = let! hasAnother = e.MoveNextAsync() cont <- hasAnother else // Starting the ham - if exclusive then - yield e.Current // return the item as it does not meet the condition for skipping + match whileKind with + | Exclusive -> yield e.Current // return the item as it does not meet the condition for skipping + | Inclusive -> () while! e.MoveNextAsync() do // propagate the rest yield e.Current @@ -822,8 +818,9 @@ module internal TaskSeqInternal = let! hasAnother = e.MoveNextAsync() cont <- hasAnother | false -> // We're starting the ham - if exclusive then - yield e.Current // Yield the one that just failed the skip test + match whileKind with + | Exclusive -> yield e.Current // return the item as it does not meet the condition for skipping + | Inclusive -> () while! e.MoveNextAsync() do // propagate the rest yield e.Current From ad2f66e2b87fd7527178664a5e4c5615447f40b9 Mon Sep 17 00:00:00 2001 From: Ruben Bartelink Date: Fri, 22 Dec 2023 17:58:12 +0000 Subject: [PATCH 16/19] Collapse takeWhile --- src/FSharp.Control.TaskSeq/TaskSeqInternal.fs | 67 +++++++------------ 1 file changed, 23 insertions(+), 44 deletions(-) diff --git a/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs b/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs index fdbaff73..6e292d4d 100644 --- a/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs +++ b/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs @@ -732,56 +732,35 @@ module internal TaskSeqInternal = taskSeq { use e = source.GetAsyncEnumerator CancellationToken.None let! notEmpty = e.MoveNextAsync() - let mutable more = notEmpty + let mutable cont = notEmpty - match whileKind, predicate with - | Exclusive, Predicate predicate -> // takeWhile - while more do - let value = e.Current - more <- predicate value - - if more then - // yield ONLY if predicate is true - yield value - let! hasMore = e.MoveNextAsync() - more <- hasMore - - | Inclusive, Predicate predicate -> // takeWhileInclusive - while more do - let value = e.Current - more <- predicate value - - // yield regardless of result of predicate - yield value - - if more then + match predicate with + | Predicate predicate -> // takeWhile(Inclusive)? + while cont do + if predicate e.Current then + yield e.Current let! hasMore = e.MoveNextAsync() - more <- hasMore + cont <- hasMore + else + match whileKind with + | Inclusive -> yield e.Current + | Exclusive -> () - | Exclusive, PredicateAsync predicate -> // takeWhileAsync - while more do - let value = e.Current - let! passed = predicate value - more <- passed + cont <- false - if more then - // yield ONLY if predicate is true - yield value + | PredicateAsync predicate -> // takeWhile(Inclusive)?Async + while cont do + match! predicate e.Current with + | true -> + yield e.Current let! hasMore = e.MoveNextAsync() - more <- hasMore - - | Inclusive, PredicateAsync predicate -> // takeWhileInclusiveAsync - while more do - let value = e.Current - let! passed = predicate value - more <- passed - - // yield regardless of predicate - yield value + cont <- hasMore + | false -> + match whileKind with + | Inclusive -> yield e.Current + | Exclusive -> () - if more then - let! hasMore = e.MoveNextAsync() - more <- hasMore + cont <- false } let skipWhile whileKind predicate (source: TaskSeq<_>) = From 064109f4781e135620617ff578f419a862dc22d2 Mon Sep 17 00:00:00 2001 From: Ruben Bartelink Date: Fri, 22 Dec 2023 18:00:19 +0000 Subject: [PATCH 17/19] f skipWhile comment --- src/FSharp.Control.TaskSeq/TaskSeqInternal.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs b/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs index 6e292d4d..b2c44db9 100644 --- a/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs +++ b/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs @@ -790,7 +790,7 @@ module internal TaskSeqInternal = yield e.Current cont <- false - | PredicateAsync predicate -> // skipWhileAsync + | PredicateAsync predicate -> // skipWhile(Inclusive)?Async while cont do match! predicate e.Current with | true -> From 82b4e892a5e83c5339adeaf7df0192bb51b47c66 Mon Sep 17 00:00:00 2001 From: Ruben Bartelink Date: Fri, 22 Dec 2023 23:13:39 +0000 Subject: [PATCH 18/19] move matching out --- src/FSharp.Control.TaskSeq/TaskSeqInternal.fs | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs b/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs index b2c44db9..c107d14a 100644 --- a/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs +++ b/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs @@ -734,6 +734,11 @@ module internal TaskSeqInternal = let! notEmpty = e.MoveNextAsync() let mutable cont = notEmpty + let inclusive = + match whileKind with + | Inclusive -> true + | Exclusive -> false + match predicate with | Predicate predicate -> // takeWhile(Inclusive)? while cont do @@ -742,9 +747,8 @@ module internal TaskSeqInternal = let! hasMore = e.MoveNextAsync() cont <- hasMore else - match whileKind with - | Inclusive -> yield e.Current - | Exclusive -> () + if inclusive then + yield e.Current cont <- false @@ -756,9 +760,8 @@ module internal TaskSeqInternal = let! hasMore = e.MoveNextAsync() cont <- hasMore | false -> - match whileKind with - | Inclusive -> yield e.Current - | Exclusive -> () + if inclusive then + yield e.Current cont <- false } @@ -773,6 +776,11 @@ module internal TaskSeqInternal = | false -> () // Nothing further to do, no matter what the rules are | true -> + let exclusive = + match whileKind with + | Exclusive -> true + | Inclusive -> false + let mutable cont = true match predicate with @@ -782,9 +790,8 @@ module internal TaskSeqInternal = let! hasAnother = e.MoveNextAsync() cont <- hasAnother else // Starting the ham - match whileKind with - | Exclusive -> yield e.Current // return the item as it does not meet the condition for skipping - | Inclusive -> () + if exclusive then + yield e.Current // return the item as it does not meet the condition for skipping while! e.MoveNextAsync() do // propagate the rest yield e.Current @@ -797,9 +804,8 @@ module internal TaskSeqInternal = let! hasAnother = e.MoveNextAsync() cont <- hasAnother | false -> // We're starting the ham - match whileKind with - | Exclusive -> yield e.Current // return the item as it does not meet the condition for skipping - | Inclusive -> () + if exclusive then + yield e.Current // return the item as it does not meet the condition for skipping while! e.MoveNextAsync() do // propagate the rest yield e.Current From 8e04745ab4d38d68e819cca13438ae16ff541743 Mon Sep 17 00:00:00 2001 From: Ruben Bartelink Date: Sun, 24 Dec 2023 12:37:06 +0000 Subject: [PATCH 19/19] Reset stuff calved off --- src/FSharp.Control.TaskSeq/TaskSeqInternal.fs | 245 +++++++++++++----- 1 file changed, 183 insertions(+), 62 deletions(-) diff --git a/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs b/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs index c107d14a..ed92a9fe 100644 --- a/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs +++ b/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs @@ -86,7 +86,7 @@ module internal TaskSeqInternal = let empty<'T> = { new IAsyncEnumerable<'T> with - member _.GetAsyncEnumerator _ = + member _.GetAsyncEnumerator(_) = { new IAsyncEnumerator<'T> with member _.MoveNextAsync() = ValueTask.False member _.Current = Unchecked.defaultof<'T> @@ -96,7 +96,7 @@ module internal TaskSeqInternal = let singleton (value: 'T) = { new IAsyncEnumerable<'T> with - member _.GetAsyncEnumerator _ = + member _.GetAsyncEnumerator(_) = let mutable status = BeforeAll { new IAsyncEnumerator<'T> with @@ -163,7 +163,6 @@ module internal TaskSeqInternal = checkNonNull (nameof source) source task { - use e = source.GetAsyncEnumerator CancellationToken.None let mutable go = true let mutable i = 0 @@ -178,6 +177,77 @@ module internal TaskSeqInternal = return i } + let inline maxMin ([] maxOrMin) (source: TaskSeq<_>) = + checkNonNull (nameof source) source + + task { + use e = source.GetAsyncEnumerator CancellationToken.None + let! nonEmpty = e.MoveNextAsync() + + if not nonEmpty then + raiseEmptySeq () + + let mutable acc = e.Current + + while! e.MoveNextAsync() do + acc <- maxOrMin e.Current acc + + return acc + } + + // 'compare' is either `<` or `>` (i.e, less-than, greater-than resp.) + let inline maxMinBy ([] compare) ([] projection) (source: TaskSeq<_>) = + checkNonNull (nameof source) source + + task { + use e = source.GetAsyncEnumerator CancellationToken.None + let! nonEmpty = e.MoveNextAsync() + + if not nonEmpty then + raiseEmptySeq () + + let value = e.Current + let mutable accProjection = projection value + let mutable accValue = value + + while! e.MoveNextAsync() do + let value = e.Current + let currentProjection = projection value + + if compare accProjection currentProjection then + accProjection <- currentProjection + accValue <- value + + return accValue + } + + // 'compare' is either `<` or `>` (i.e, less-than, greater-than resp.) + let inline maxMinByAsync ([] compare) ([] projectionAsync) (source: TaskSeq<_>) = + checkNonNull (nameof source) source + + task { + use e = source.GetAsyncEnumerator CancellationToken.None + let! nonEmpty = e.MoveNextAsync() + + if not nonEmpty then + raiseEmptySeq () + + let value = e.Current + let! projValue = projectionAsync value + let mutable accProjection = projValue + let mutable accValue = value + + while! e.MoveNextAsync() do + let value = e.Current + let! currentProjection = projectionAsync value + + if compare accProjection currentProjection then + accProjection <- currentProjection + accValue <- value + + return accValue + } + let tryExactlyOne (source: TaskSeq<_>) = checkNonNull (nameof source) source @@ -732,38 +802,56 @@ module internal TaskSeqInternal = taskSeq { use e = source.GetAsyncEnumerator CancellationToken.None let! notEmpty = e.MoveNextAsync() - let mutable cont = notEmpty + let mutable more = notEmpty - let inclusive = - match whileKind with - | Inclusive -> true - | Exclusive -> false + match whileKind, predicate with + | Exclusive, Predicate predicate -> // takeWhile + while more do + let value = e.Current + more <- predicate value - match predicate with - | Predicate predicate -> // takeWhile(Inclusive)? - while cont do - if predicate e.Current then - yield e.Current + if more then + // yield ONLY if predicate is true + yield value let! hasMore = e.MoveNextAsync() - cont <- hasMore - else - if inclusive then - yield e.Current + more <- hasMore - cont <- false + | Inclusive, Predicate predicate -> // takeWhileInclusive + while more do + let value = e.Current + more <- predicate value - | PredicateAsync predicate -> // takeWhile(Inclusive)?Async - while cont do - match! predicate e.Current with - | true -> - yield e.Current + // yield regardless of result of predicate + yield value + + if more then let! hasMore = e.MoveNextAsync() - cont <- hasMore - | false -> - if inclusive then - yield e.Current + more <- hasMore + + | Exclusive, PredicateAsync predicate -> // takeWhileAsync + while more do + let value = e.Current + let! passed = predicate value + more <- passed + + if more then + // yield ONLY if predicate is true + yield value + let! hasMore = e.MoveNextAsync() + more <- hasMore - cont <- false + | Inclusive, PredicateAsync predicate -> // takeWhileInclusiveAsync + while more do + let value = e.Current + let! passed = predicate value + more <- passed + + // yield regardless of predicate + yield value + + if more then + let! hasMore = e.MoveNextAsync() + more <- hasMore } let skipWhile whileKind predicate (source: TaskSeq<_>) = @@ -771,46 +859,77 @@ module internal TaskSeqInternal = taskSeq { use e = source.GetAsyncEnumerator CancellationToken.None + let! moveFirst = e.MoveNextAsync() + let mutable more = moveFirst - match! e.MoveNextAsync() with - | false -> () // Nothing further to do, no matter what the rules are - | true -> + match whileKind, predicate with + | Exclusive, Predicate predicate -> // skipWhile + while more && predicate e.Current do + let! hasMore = e.MoveNextAsync() + more <- hasMore - let exclusive = - match whileKind with - | Exclusive -> true - | Inclusive -> false + if more then + // yield the last one where the predicate was false + // (this ensures we skip 0 or more) + yield e.Current + while! e.MoveNextAsync() do // get the rest + yield e.Current + + | Inclusive, Predicate predicate -> // skipWhileInclusive + while more && predicate e.Current do + let! hasMore = e.MoveNextAsync() + more <- hasMore + + if more then + // yield the rest (this ensures we skip 1 or more) + while! e.MoveNextAsync() do + yield e.Current + + | Exclusive, PredicateAsync predicate -> // skipWhileAsync let mutable cont = true - match predicate with - | Predicate predicate -> // skipWhile(Inclusive)? - while cont do - if predicate e.Current then // spam -> skip - let! hasAnother = e.MoveNextAsync() - cont <- hasAnother - else // Starting the ham - if exclusive then - yield e.Current // return the item as it does not meet the condition for skipping + if more then + let! hasMore = predicate e.Current + cont <- hasMore - while! e.MoveNextAsync() do // propagate the rest - yield e.Current + while more && cont do + let! moveNext = e.MoveNextAsync() - cont <- false - | PredicateAsync predicate -> // skipWhile(Inclusive)?Async - while cont do - match! predicate e.Current with - | true -> - let! hasAnother = e.MoveNextAsync() - cont <- hasAnother - | false -> // We're starting the ham - if exclusive then - yield e.Current // return the item as it does not meet the condition for skipping + if moveNext then + let! hasMore = predicate e.Current + cont <- hasMore - while! e.MoveNextAsync() do // propagate the rest - yield e.Current + more <- moveNext - cont <- false + if more then + // yield the last one where the predicate was false + // (this ensures we skip 0 or more) + yield e.Current + + while! e.MoveNextAsync() do // get the rest + yield e.Current + + | Inclusive, PredicateAsync predicate -> // skipWhileInclusiveAsync + let mutable cont = true + + if more then + let! hasMore = predicate e.Current + cont <- hasMore + + while more && cont do + let! moveNext = e.MoveNextAsync() + + if moveNext then + let! hasMore = predicate e.Current + cont <- hasMore + + more <- moveNext + + if more then + // get the rest, this gives 1 or more semantics + while! e.MoveNextAsync() do + yield e.Current } // Consider turning using an F# version of this instead? @@ -859,11 +978,13 @@ module internal TaskSeqInternal = go <- step } - interface IDisposable with - override _.Dispose() = - if isNull _rwLock |> not then + interface IAsyncDisposable with + override _.DisposeAsync() = + if not (isNull _rwLock) then _rwLock.Dispose() + ValueTask.CompletedTask + let except itemsToExclude (source: TaskSeq<_>) = checkNonNull (nameof source) source checkNonNull (nameof itemsToExclude) itemsToExclude