Skip to content

Commit

Permalink
Implement TaskSeq.forall and forallAsync
Browse files Browse the repository at this point in the history
  • Loading branch information
abelbraaksma committed Mar 16, 2024
1 parent 3d2c998 commit 41fe733
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 4 deletions.
3 changes: 3 additions & 0 deletions src/FSharp.Control.TaskSeq/TaskSeq.fs
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,9 @@ type TaskSeq private () =
static member except itemsToExclude source = Internal.except itemsToExclude source
static member exceptOfSeq itemsToExclude source = Internal.exceptOfSeq itemsToExclude source

static member forall predicate source = Internal.forall (Predicate predicate) source
static member forallAsync predicate source = Internal.forall (PredicateAsync predicate) source

static member exists predicate source =
Internal.tryFind (Predicate predicate) source
|> Task.map Option.isSome
Expand Down
24 changes: 24 additions & 0 deletions src/FSharp.Control.TaskSeq/TaskSeq.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -875,6 +875,30 @@ type TaskSeq =
/// <exception cref="T:ArgumentNullException">Thrown when the input task sequence is null.</exception>
static member whereAsync: predicate: ('T -> #Task<bool>) -> source: TaskSeq<'T> -> TaskSeq<'T>

/// <summary>
/// Tests if all elements of the sequence satisfy the given predicate. Stops evaluating
/// as soon as <paramref name="predicate" /> returns <see cref="false" />.
/// If <paramref name="predicate" /> is asynchronous, consider using <see cref="TaskSeq.forallAsync" />.
/// </summary>
///
/// <param name="predicate">A function to test an element of the input sequence.</param>
/// <param name="source">The input task sequence.</param>
/// <returns>A task that, after awaiting, holds true if every element of the sequence satisfies the predicate; false otherwise.</returns>
/// <exception cref="T:ArgumentNullException">Thrown when the input task sequence is null.</exception>
static member forall: predicate: ('T -> bool) -> source: TaskSeq<'T> -> Task<bool>

/// <summary>
/// Tests if all elements of the sequence satisfy the given asynchronous predicate. Stops evaluating
/// as soon as <paramref name="predicate" /> returns <see cref="false" />.
/// If <paramref name="predicate" /> is synchronous, consider using <see cref="TaskSeq.forall" />.
/// </summary>
///
/// <param name="predicate">A function to test an element of the input sequence.</param>
/// <param name="source">The input task sequence.</param>
/// <returns>A task that, after awaiting, holds true if every element of the sequence satisfies the predicate; false otherwise.</returns>
/// <exception cref="T:ArgumentNullException">Thrown when the input task sequence is null.</exception>
static member forallAsync: predicate: ('T -> #Task<bool>) -> source: TaskSeq<'T> -> Task<bool>

/// <summary>
/// Returns a task sequence that, when iterated, skips <paramref name="count" /> elements of the underlying
/// sequence, and then yields the remainder. Raises an exception if there are not <paramref name="count" />
Expand Down
44 changes: 40 additions & 4 deletions src/FSharp.Control.TaskSeq/TaskSeqInternal.fs
Original file line number Diff line number Diff line change
Expand Up @@ -690,18 +690,54 @@ module internal TaskSeqInternal =

taskSeq {
match predicate with
| Predicate predicate ->
| Predicate syncPredicate ->
for item in source do
if predicate item then
if syncPredicate item then
yield item

| PredicateAsync predicate ->
| PredicateAsync asyncPredicate ->
for item in source do
match! predicate item with
match! asyncPredicate item with
| true -> yield item
| false -> ()
}

let forall predicate (source: TaskSeq<_>) =
checkNonNull (nameof source) source

match predicate with
| Predicate syncPredicate -> task {
use e = source.GetAsyncEnumerator CancellationToken.None
let mutable state = true
let! cont = e.MoveNextAsync()
let mutable hasMore = cont

while state && hasMore do
state <- syncPredicate e.Current

if state then
let! cont = e.MoveNextAsync()
hasMore <- cont

return state
}

| PredicateAsync asyncPredicate -> task {
use e = source.GetAsyncEnumerator CancellationToken.None
let mutable state = true
let! cont = e.MoveNextAsync()
let mutable hasMore = cont

while state && hasMore do
let! pred = asyncPredicate e.Current
state <- pred

if state then
let! cont = e.MoveNextAsync()
hasMore <- cont

return state
}

let skipOrTake skipOrTake count (source: TaskSeq<_>) =
checkNonNull (nameof source) source
Expand Down

0 comments on commit 41fe733

Please sign in to comment.