diff --git a/release-notes.txt b/release-notes.txt
index 7078840..0930bca 100644
--- a/release-notes.txt
+++ b/release-notes.txt
@@ -2,6 +2,7 @@
Release notes:
1.0.0
+ - adds TaskSeq.findBack, findBackAsync, tryFindBack, tryFindBackAsync, findIndexBack, findIndexBackAsync, tryFindIndexBack, tryFindIndexBackAsync
- fixes: TaskSeq.insertAt, insertManyAt, removeAt, removeManyAt, updateAt now raise ArgumentNullException (not NullReferenceException) when given a null source; insertManyAt also validates the values argument
- refactor: simplify lengthBy and lengthBeforeMax to use while! and remove the redundant mutable 'go' and initial MoveNextAsync
- adds TaskSeq.distinctUntilChangedWith and TaskSeq.distinctUntilChangedWithAsync, #345
diff --git a/src/FSharp.Control.TaskSeq.Test/FSharp.Control.TaskSeq.Test.fsproj b/src/FSharp.Control.TaskSeq.Test/FSharp.Control.TaskSeq.Test.fsproj
index 58856f1..cdd301a 100644
--- a/src/FSharp.Control.TaskSeq.Test/FSharp.Control.TaskSeq.Test.fsproj
+++ b/src/FSharp.Control.TaskSeq.Test/FSharp.Control.TaskSeq.Test.fsproj
@@ -25,6 +25,7 @@
+
diff --git a/src/FSharp.Control.TaskSeq.Test/TaskSeq.FindBack.Tests.fs b/src/FSharp.Control.TaskSeq.Test/TaskSeq.FindBack.Tests.fs
new file mode 100644
index 0000000..11921bb
--- /dev/null
+++ b/src/FSharp.Control.TaskSeq.Test/TaskSeq.FindBack.Tests.fs
@@ -0,0 +1,345 @@
+module TaskSeq.Tests.FindBack
+
+open System.Collections.Generic
+
+open Xunit
+open FsUnit.Xunit
+
+open FSharp.Control
+
+//
+// TaskSeq.findBack
+// TaskSeq.findBackAsync
+// TaskSeq.tryFindBack
+// TaskSeq.tryFindBackAsync
+// TaskSeq.findIndexBack
+// TaskSeq.findIndexBackAsync
+// TaskSeq.tryFindIndexBack
+// TaskSeq.tryFindIndexBackAsync
+//
+
+module EmptySeq =
+ []
+ let ``Null source is invalid`` () =
+ assertNullArg
+ <| fun () -> TaskSeq.findBack (fun _ -> false) null
+
+ assertNullArg
+ <| fun () -> TaskSeq.findBackAsync (fun _ -> Task.fromResult false) null
+
+ assertNullArg
+ <| fun () -> TaskSeq.tryFindBack (fun _ -> false) null
+
+ assertNullArg
+ <| fun () -> TaskSeq.tryFindBackAsync (fun _ -> Task.fromResult false) null
+
+ assertNullArg
+ <| fun () -> TaskSeq.findIndexBack (fun _ -> false) null
+
+ assertNullArg
+ <| fun () -> TaskSeq.findIndexBackAsync (fun _ -> Task.fromResult false) null
+
+ assertNullArg
+ <| fun () -> TaskSeq.tryFindIndexBack (fun _ -> false) null
+
+ assertNullArg
+ <| fun () -> TaskSeq.tryFindIndexBackAsync (fun _ -> Task.fromResult false) null
+
+ [)>]
+ let ``TaskSeq-findBack raises KeyNotFoundException`` variant =
+ fun () ->
+ Gen.getEmptyVariant variant
+ |> TaskSeq.findBack ((=) 12)
+ |> Task.ignore
+ |> should throwAsyncExact typeof
+
+ [)>]
+ let ``TaskSeq-findBackAsync raises KeyNotFoundException`` variant =
+ fun () ->
+ Gen.getEmptyVariant variant
+ |> TaskSeq.findBackAsync (fun x -> task { return x = 12 })
+ |> Task.ignore
+ |> should throwAsyncExact typeof
+
+ [)>]
+ let ``TaskSeq-tryFindBack returns None`` variant =
+ Gen.getEmptyVariant variant
+ |> TaskSeq.tryFindBack ((=) 12)
+ |> Task.map (should be None')
+
+ [)>]
+ let ``TaskSeq-tryFindBackAsync returns None`` variant =
+ Gen.getEmptyVariant variant
+ |> TaskSeq.tryFindBackAsync (fun x -> task { return x = 12 })
+ |> Task.map (should be None')
+
+ [)>]
+ let ``TaskSeq-findIndexBack raises KeyNotFoundException`` variant =
+ fun () ->
+ Gen.getEmptyVariant variant
+ |> TaskSeq.findIndexBack ((=) 12)
+ |> Task.ignore
+ |> should throwAsyncExact typeof
+
+ [)>]
+ let ``TaskSeq-findIndexBackAsync raises KeyNotFoundException`` variant =
+ fun () ->
+ Gen.getEmptyVariant variant
+ |> TaskSeq.findIndexBackAsync (fun x -> task { return x = 12 })
+ |> Task.ignore
+ |> should throwAsyncExact typeof
+
+ [)>]
+ let ``TaskSeq-tryFindIndexBack returns None`` variant =
+ Gen.getEmptyVariant variant
+ |> TaskSeq.tryFindIndexBack ((=) 12)
+ |> Task.map (should be None')
+
+ [)>]
+ let ``TaskSeq-tryFindIndexBackAsync returns None`` variant =
+ Gen.getEmptyVariant variant
+ |> TaskSeq.tryFindIndexBackAsync (fun x -> task { return x = 12 })
+ |> Task.map (should be None')
+
+module Immutable =
+ [)>]
+ let ``TaskSeq-findBack sad path raises KeyNotFoundException`` variant =
+ fun () ->
+ Gen.getSeqImmutable variant
+ |> TaskSeq.findBack ((=) 0) // dummy tasks sequence starts at 1
+ |> Task.ignore
+
+ |> should throwAsyncExact typeof
+
+ [)>]
+ let ``TaskSeq-findBackAsync sad path raises KeyNotFoundException`` variant =
+ fun () ->
+ Gen.getSeqImmutable variant
+ |> TaskSeq.findBackAsync (fun x -> task { return x = 0 }) // dummy tasks sequence starts at 1
+ |> Task.ignore
+
+ |> should throwAsyncExact typeof
+
+ [)>]
+ let ``TaskSeq-findBack happy path returns last match`` variant =
+ Gen.getSeqImmutable variant
+ |> TaskSeq.findBack (fun x -> x < 6 && x > 3) // matches 4, 5 — last is 5
+ |> Task.map (should equal 5)
+
+ [)>]
+ let ``TaskSeq-findBackAsync happy path returns last match`` variant =
+ Gen.getSeqImmutable variant
+ |> TaskSeq.findBackAsync (fun x -> task { return x < 6 && x > 3 }) // matches 4, 5 — last is 5
+ |> Task.map (should equal 5)
+
+ [)>]
+ let ``TaskSeq-findBack happy path returns last item`` variant =
+ Gen.getSeqImmutable variant
+ |> TaskSeq.findBack (fun x -> x <= 10) // all items qualify; last is 10
+ |> Task.map (should equal 10)
+
+ [)>]
+ let ``TaskSeq-findBackAsync happy path returns last item`` variant =
+ Gen.getSeqImmutable variant
+ |> TaskSeq.findBackAsync (fun x -> task { return x <= 10 }) // all items qualify; last is 10
+ |> Task.map (should equal 10)
+
+ [)>]
+ let ``TaskSeq-findBack happy path returns only matching item`` variant =
+ Gen.getSeqImmutable variant
+ |> TaskSeq.findBack ((=) 7) // exactly one match
+ |> Task.map (should equal 7)
+
+ [)>]
+ let ``TaskSeq-findIndexBack sad path raises KeyNotFoundException`` variant =
+ fun () ->
+ Gen.getSeqImmutable variant
+ |> TaskSeq.findIndexBack ((=) 0) // dummy tasks sequence starts at 1
+ |> Task.ignore
+
+ |> should throwAsyncExact typeof
+
+ [)>]
+ let ``TaskSeq-findIndexBackAsync sad path raises KeyNotFoundException`` variant =
+ fun () ->
+ Gen.getSeqImmutable variant
+ |> TaskSeq.findIndexBackAsync (fun x -> task { return x = 0 }) // dummy tasks sequence starts at 1
+ |> Task.ignore
+
+ |> should throwAsyncExact typeof
+
+ [)>]
+ let ``TaskSeq-findIndexBack happy path returns last matching index`` variant =
+ Gen.getSeqImmutable variant
+ |> TaskSeq.findIndexBack (fun x -> x < 6 && x > 3) // matches indices 3, 4 — last is 4
+ |> Task.map (should equal 4)
+
+ [)>]
+ let ``TaskSeq-findIndexBackAsync happy path returns last matching index`` variant =
+ Gen.getSeqImmutable variant
+ |> TaskSeq.findIndexBackAsync (fun x -> task { return x < 6 && x > 3 }) // matches indices 3, 4 — last is 4
+ |> Task.map (should equal 4)
+
+ [)>]
+ let ``TaskSeq-findIndexBack happy path returns last index when all match`` variant =
+ Gen.getSeqImmutable variant
+ |> TaskSeq.findIndexBack (fun x -> x <= 10) // all 10 items qualify; last index is 9
+ |> Task.map (should equal 9)
+
+ [)>]
+ let ``TaskSeq-findIndexBack happy path single match`` variant =
+ Gen.getSeqImmutable variant
+ |> TaskSeq.findIndexBack ((=) 1) // value 1 is at index 0
+ |> Task.map (should equal 0)
+
+ [)>]
+ let ``TaskSeq-tryFindBack sad path returns None`` variant =
+ Gen.getSeqImmutable variant
+ |> TaskSeq.tryFindBack ((=) 0) // dummy tasks sequence starts at 1
+ |> Task.map (should be None')
+
+ [)>]
+ let ``TaskSeq-tryFindBackAsync sad path returns None`` variant =
+ Gen.getSeqImmutable variant
+ |> TaskSeq.tryFindBackAsync (fun x -> task { return x = 0 }) // dummy tasks sequence starts at 1
+ |> Task.map (should be None')
+
+ [)>]
+ let ``TaskSeq-tryFindBack happy path returns last match`` variant =
+ Gen.getSeqImmutable variant
+ |> TaskSeq.tryFindBack (fun x -> x < 6 && x > 3)
+ |> Task.map (should equal (Some 5))
+
+ [)>]
+ let ``TaskSeq-tryFindBackAsync happy path returns last match`` variant =
+ Gen.getSeqImmutable variant
+ |> TaskSeq.tryFindBackAsync (fun x -> task { return x < 6 && x > 3 })
+ |> Task.map (should equal (Some 5))
+
+ [)>]
+ let ``TaskSeq-tryFindIndexBack sad path returns None`` variant =
+ Gen.getSeqImmutable variant
+ |> TaskSeq.tryFindIndexBack ((=) 0)
+ |> Task.map (should be None')
+
+ [)>]
+ let ``TaskSeq-tryFindIndexBackAsync sad path returns None`` variant =
+ Gen.getSeqImmutable variant
+ |> TaskSeq.tryFindIndexBackAsync (fun x -> task { return x = 0 })
+ |> Task.map (should be None')
+
+ [)>]
+ let ``TaskSeq-tryFindIndexBack happy path returns last matching index`` variant =
+ Gen.getSeqImmutable variant
+ |> TaskSeq.tryFindIndexBack (fun x -> x < 6 && x > 3)
+ |> Task.map (should equal (Some 4))
+
+ [)>]
+ let ``TaskSeq-tryFindIndexBackAsync happy path returns last matching index`` variant =
+ Gen.getSeqImmutable variant
+ |> TaskSeq.tryFindIndexBackAsync (fun x -> task { return x < 6 && x > 3 })
+ |> Task.map (should equal (Some 4))
+
+module SideEffects =
+ [)>]
+ let ``TaskSeq-findBack consumes the entire sequence`` variant =
+ Gen.getSeqWithSideEffect variant
+ |> TaskSeq.findBack (fun x -> x < 6 && x > 3)
+ |> Task.map (should equal 5)
+
+ [)>]
+ let ``TaskSeq-tryFindBack consumes the entire sequence`` variant =
+ Gen.getSeqWithSideEffect variant
+ |> TaskSeq.tryFindBack (fun x -> x < 6 && x > 3)
+ |> Task.map (should equal (Some 5))
+
+ [)>]
+ let ``TaskSeq-findIndexBack consumes the entire sequence`` variant =
+ Gen.getSeqWithSideEffect variant
+ |> TaskSeq.findIndexBack (fun x -> x < 6 && x > 3)
+ |> Task.map (should equal 4)
+
+ [)>]
+ let ``TaskSeq-tryFindIndexBack consumes the entire sequence`` variant =
+ Gen.getSeqWithSideEffect variant
+ |> TaskSeq.tryFindIndexBack (fun x -> x < 6 && x > 3)
+ |> Task.map (should equal (Some 4))
+
+ []
+ let ``TaskSeq-findBack _specialcase_ unlike findBack, findBack always evaluates the full sequence`` () = task {
+ let mutable i = 0
+
+ let ts = taskSeq {
+ yield 42
+ i <- i + 1 // side effect after the matching yield
+ yield 1 // an item after the match
+ }
+
+ // findBack must find the LAST match so it evaluates everything
+ let! found = ts |> TaskSeq.findBack ((=) 42)
+ found |> should equal 42
+ i |> should equal 1 // side effect WAS executed — findBack consumed all items
+ }
+
+ []
+ let ``TaskSeq-tryFindBack _specialcase_ always evaluates the full sequence`` () = task {
+ let mutable i = 0
+
+ let ts = taskSeq {
+ yield 42
+ i <- i + 1
+ yield 1
+ }
+
+ let! found = ts |> TaskSeq.tryFindBack ((=) 42)
+ found |> should equal (Some 42)
+ i |> should equal 1 // side effect WAS executed
+ }
+
+ []
+ let ``TaskSeq-findBack _specialcase_ returns the last of multiple matches`` () = task {
+ let ts = taskSeq { yield! [ 3; 5; 3; 7; 3 ] }
+
+ let! found = ts |> TaskSeq.findBack ((=) 3)
+ found |> should equal 3 // value is 3 (last of three matches)
+ }
+
+ []
+ let ``TaskSeq-findIndexBack _specialcase_ returns the last matching index among multiple matches`` () = task {
+ let ts = taskSeq { yield! [ 3; 5; 3; 7; 3 ] }
+
+ let! found = ts |> TaskSeq.findIndexBack ((=) 3)
+ found |> should equal 4 // index 4 is the last 3
+ }
+
+ []
+ let ``TaskSeq-tryFindBack _specialcase_ returns last match when multiple exist`` () = task {
+ let ts = taskSeq { yield! [ 1; 2; 3; 4; 5; 4; 3; 2; 1 ] }
+
+ let! found = ts |> TaskSeq.tryFindBack (fun x -> x > 3)
+ found |> should equal (Some 4) // last element > 3 is the 4 at index 5
+ }
+
+ []
+ let ``TaskSeq-tryFindIndexBack _specialcase_ returns last matching index when multiple exist`` () = task {
+ let ts = taskSeq { yield! [ 1; 2; 3; 4; 5; 4; 3; 2; 1 ] }
+
+ let! found = ts |> TaskSeq.tryFindIndexBack (fun x -> x > 3)
+ found |> should equal (Some 5) // last element > 3 is at index 5
+ }
+
+ []
+ let ``TaskSeq-findIndexBack _specialcase_ single-element sequence`` () = task {
+ let ts = taskSeq { yield 42 }
+
+ let! found = ts |> TaskSeq.findIndexBack ((=) 42)
+ found |> should equal 0
+ }
+
+ []
+ let ``TaskSeq-tryFindBack _specialcase_ single-element no match returns None`` () = task {
+ let ts = taskSeq { yield 42 }
+
+ let! found = ts |> TaskSeq.tryFindBack ((=) 99)
+ found |> should be None'
+ }
diff --git a/src/FSharp.Control.TaskSeq/TaskSeq.fs b/src/FSharp.Control.TaskSeq/TaskSeq.fs
index 9051ddc..c567c31 100644
--- a/src/FSharp.Control.TaskSeq/TaskSeq.fs
+++ b/src/FSharp.Control.TaskSeq/TaskSeq.fs
@@ -469,6 +469,11 @@ type TaskSeq private () =
static member tryFindIndex predicate source = Internal.tryFindIndex (Predicate predicate) source
static member tryFindIndexAsync predicate source = Internal.tryFindIndex (PredicateAsync predicate) source
+ static member tryFindBack predicate source = Internal.tryFindBack (Predicate predicate) source
+ static member tryFindBackAsync predicate source = Internal.tryFindBack (PredicateAsync predicate) source
+ static member tryFindIndexBack predicate source = Internal.tryFindIndexBack (Predicate predicate) source
+ static member tryFindIndexBackAsync predicate source = Internal.tryFindIndexBack (PredicateAsync predicate) source
+
static member insertAt index value source = Internal.insertAt index (One value) source
static member insertManyAt index values source = Internal.insertAt index (Many values) source
static member removeAt index source = Internal.removeAt index source
@@ -524,6 +529,22 @@ type TaskSeq private () =
Internal.tryFindIndex (PredicateAsync predicate) source
|> Task.map (Option.defaultWith Internal.raiseNotFound)
+ static member findBack predicate source =
+ Internal.tryFindBack (Predicate predicate) source
+ |> Task.map (Option.defaultWith Internal.raiseNotFound)
+
+ static member findBackAsync predicate source =
+ Internal.tryFindBack (PredicateAsync predicate) source
+ |> Task.map (Option.defaultWith Internal.raiseNotFound)
+
+ static member findIndexBack predicate source =
+ Internal.tryFindIndexBack (Predicate predicate) source
+ |> Task.map (Option.defaultWith Internal.raiseNotFound)
+
+ static member findIndexBackAsync predicate source =
+ Internal.tryFindIndexBack (PredicateAsync predicate) source
+ |> Task.map (Option.defaultWith Internal.raiseNotFound)
+
//
// zip/unzip/fold etc functions
//
diff --git a/src/FSharp.Control.TaskSeq/TaskSeq.fsi b/src/FSharp.Control.TaskSeq/TaskSeq.fsi
index f914048..7542081 100644
--- a/src/FSharp.Control.TaskSeq/TaskSeq.fsi
+++ b/src/FSharp.Control.TaskSeq/TaskSeq.fsi
@@ -1365,6 +1365,58 @@ type TaskSeq =
/// Thrown when the input task sequence is null.
static member tryFindIndexAsync: predicate: ('T -> #Task) -> source: TaskSeq<'T> -> Task
+ ///
+ /// Returns the last element for which the given function returns
+ /// . Returns if no such element exists.
+ /// The entire sequence is consumed. If is asynchronous, consider
+ /// using .
+ ///
+ ///
+ /// A function that evaluates to a when given an item in the sequence.
+ /// The input task sequence.
+ /// The last element for which the predicate returns , or .
+ /// Thrown when the input task sequence is null.
+ static member tryFindBack: predicate: ('T -> bool) -> source: TaskSeq<'T> -> Task<'T option>
+
+ ///
+ /// Returns the last element for which the given asynchronous function returns
+ /// . Returns if no such element exists.
+ /// The entire sequence is consumed. If is synchronous, consider
+ /// using .
+ ///
+ ///
+ /// An asynchronous function that evaluates to a when given an item in the sequence.
+ /// The input task sequence.
+ /// The last element for which the predicate returns , or .
+ /// Thrown when the input task sequence is null.
+ static member tryFindBackAsync: predicate: ('T -> #Task) -> source: TaskSeq<'T> -> Task<'T option>
+
+ ///
+ /// Returns the index, starting from zero, of the last element for which the given function
+ /// returns . Returns if no such element exists.
+ /// The entire sequence is consumed. If is asynchronous, consider
+ /// using .
+ ///
+ ///
+ /// A function that evaluates to a when given an item in the sequence.
+ /// The input task sequence.
+ /// The last index for which the predicate returns , or .
+ /// Thrown when the input task sequence is null.
+ static member tryFindIndexBack: predicate: ('T -> bool) -> source: TaskSeq<'T> -> Task
+
+ ///
+ /// Returns the index, starting from zero, of the last element for which the given asynchronous function
+ /// returns . Returns if no such element exists.
+ /// The entire sequence is consumed. If is synchronous, consider
+ /// using .
+ ///
+ ///
+ /// An asynchronous function that evaluates to a when given an item in the sequence.
+ /// The input task sequence.
+ /// The last index for which the predicate returns , or .
+ /// Thrown when the input task sequence is null.
+ static member tryFindIndexBackAsync: predicate: ('T -> #Task) -> source: TaskSeq<'T> -> Task
+
///
/// Applies the given function to successive elements, returning the first result where
/// the function returns . Throws an exception if none is found.
@@ -1443,6 +1495,58 @@ type TaskSeq =
/// Thrown if no element returns when evaluated by the function.
static member findIndexAsync: predicate: ('T -> #Task) -> source: TaskSeq<'T> -> Task
+ ///
+ /// Returns the last element for which the given function returns .
+ /// Throws an exception if none is found. The entire sequence is consumed.
+ /// If is asynchronous, consider using .
+ ///
+ ///
+ /// A function that evaluates to a when given an item in the sequence.
+ /// The input task sequence.
+ /// The last element for which the predicate returns .
+ /// Thrown when the input task sequence is null.
+ /// Thrown if no element returns when evaluated by the function.
+ static member findBack: predicate: ('T -> bool) -> source: TaskSeq<'T> -> Task<'T>
+
+ ///
+ /// Returns the last element for which the given asynchronous function returns .
+ /// Throws an exception if none is found. The entire sequence is consumed.
+ /// If is synchronous, consider using .
+ ///
+ ///
+ /// An asynchronous function that evaluates to a when given an item in the sequence.
+ /// The input task sequence.
+ /// The last element for which the predicate returns .
+ /// Thrown when the input task sequence is null.
+ /// Thrown if no element returns when evaluated by the function.
+ static member findBackAsync: predicate: ('T -> #Task) -> source: TaskSeq<'T> -> Task<'T>
+
+ ///
+ /// Returns the index, starting from zero, of the last element for which the given function
+ /// returns . Throws an exception if none is found. The entire sequence is consumed.
+ /// If is asynchronous, consider using .
+ ///
+ ///
+ /// A function that evaluates to a when given an item in the sequence.
+ /// The input task sequence.
+ /// The last index for which the predicate returns .
+ /// Thrown when the input task sequence is null.
+ /// Thrown if no element returns when evaluated by the function.
+ static member findIndexBack: predicate: ('T -> bool) -> source: TaskSeq<'T> -> Task
+
+ ///
+ /// Returns the index, starting from zero, of the last element for which the given asynchronous function
+ /// returns . Throws an exception if none is found. The entire sequence is consumed.
+ /// If is synchronous, consider using .
+ ///
+ ///
+ /// An asynchronous function that evaluates to a when given an item in the sequence.
+ /// The input task sequence.
+ /// The last index for which the predicate returns .
+ /// Thrown when the input task sequence is null.
+ /// Thrown if no element returns when evaluated by the function.
+ static member findIndexBackAsync: predicate: ('T -> #Task) -> source: TaskSeq<'T> -> Task
+
///
/// Tests if the sequence contains the specified element. Returns
/// if contains the specified element;
diff --git a/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs b/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs
index cc279c1..2f742b0 100644
--- a/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs
+++ b/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs
@@ -1036,6 +1036,75 @@ module internal TaskSeqInternal =
if isFound then return Some index else return None
}
+ let tryFindBack predicate (source: TaskSeq<_>) =
+ checkNonNull (nameof source) source
+
+ task {
+ use e = source.GetAsyncEnumerator CancellationToken.None
+
+ let mutable go = true
+ let mutable foundItem = None
+ let! step = e.MoveNextAsync()
+ go <- step
+
+ match predicate with
+ | Predicate predicate ->
+ while go do
+ if predicate e.Current then
+ foundItem <- Some e.Current
+
+ let! step = e.MoveNextAsync()
+ go <- step
+
+ | PredicateAsync predicate ->
+ while go do
+ let! predicateResult = predicate e.Current
+
+ if predicateResult then
+ foundItem <- Some e.Current
+
+ let! step = e.MoveNextAsync()
+ go <- step
+
+ return foundItem
+ }
+
+ let tryFindIndexBack predicate (source: TaskSeq<_>) =
+ checkNonNull (nameof source) source
+
+ task {
+ use e = source.GetAsyncEnumerator CancellationToken.None
+
+ let mutable go = true
+ let mutable foundIndex = None
+ let mutable index = 0
+ let! step = e.MoveNextAsync()
+ go <- step
+
+ match predicate with
+ | Predicate predicate ->
+ while go do
+ if predicate e.Current then
+ foundIndex <- Some index
+
+ index <- index + 1
+ let! step = e.MoveNextAsync()
+ go <- step
+
+ | PredicateAsync predicate ->
+ while go do
+ let! predicateResult = predicate e.Current
+
+ if predicateResult then
+ foundIndex <- Some index
+
+ index <- index + 1
+ let! step = e.MoveNextAsync()
+ go <- step
+
+ return foundIndex
+ }
+
let choose chooser (source: TaskSeq<_>) =
checkNonNull (nameof source) source