From 7e7f134ae3a912c2abe532dc97f59ffb9fcf92b2 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Wed, 1 Apr 2026 01:32:13 +0000
Subject: [PATCH 1/2] feat: add TaskSeq.findBack, findBackAsync, tryFindBack,
tryFindBackAsync, findIndexBack, findIndexBackAsync, tryFindIndexBack,
tryFindIndexBackAsync (273 tests)
These functions find the last matching element or index, mirroring Seq.findBack,
Seq.tryFindBack, Seq.findIndexBack, and Seq.tryFindIndexBack from FSharp.Core.
Unlike their forward counterparts, they consume the entire sequence to locate
the last match; this is documented in the signatures.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
release-notes.txt | 1 +
.../FSharp.Control.TaskSeq.Test.fsproj | 1 +
.../TaskSeq.FindBack.Tests.fs | 345 ++++++++++++++++++
src/FSharp.Control.TaskSeq/TaskSeq.fs | 21 ++
src/FSharp.Control.TaskSeq/TaskSeq.fsi | 104 ++++++
src/FSharp.Control.TaskSeq/TaskSeqInternal.fs | 69 ++++
6 files changed, 541 insertions(+)
create mode 100644 src/FSharp.Control.TaskSeq.Test/TaskSeq.FindBack.Tests.fs
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
From dbaeb7693619b8ca96871e1731d3c64ab0954ac1 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
Date: Wed, 1 Apr 2026 01:32:16 +0000
Subject: [PATCH 2/2] ci: trigger checks