FsToolkit.ErrorHandling icon indicating copy to clipboard operation
FsToolkit.ErrorHandling copied to clipboard

Question: get the first success from `Async<Result<'ok, 'error>> list`?

Open njlr opened this issue 3 months ago • 3 comments

Like List.sequenceAsyncResultM but would stop at the first success.

Is there something like this already provided?

let firstSuccess (tasks : Async<Result<'ok, 'error>> list) : Async<Result<'ok, 'error list>> =
  let rec loop (errors : 'e list) tasks =
    async {
      match tasks with
      | [] ->
        return Error (List.rev errors)
      | x :: xs ->
        match! x with
        | Ok t ->
          return Ok t
        | Error error ->
          return! loop (error :: errors) xs
    }

  loop [] tasks

If not, would a PR be accepted?

Thanks!

njlr avatar Sep 24 '25 16:09 njlr

I don't think anything like this is available currently. It feels like a derivative of List.tryPick but accumulates errors. Maybe we should make that available with an additional helper to yield if the list is already a group

module List =
    module AsyncResult =
        let tryPick f tasks =
            async {
                let mutable errors = ListCollector()
                let mutable result = ValueNone


                for task in tasks |> List.takeWhile (fun _ -> result.IsNone) do
                    match! f task with
                    | Ok t -> result <- ValueSome(Ok t)
                    | Error error -> errors.Add error

                match result with
                | ValueSome okResult -> return okResult
                | ValueNone -> return Error(errors.Close())
            }

        let tryYield tasks = tryPick id tasks

TheAngryByrd avatar Sep 24 '25 19:09 TheAngryByrd

It feels like a derivative of List.tryPick but accumulates errors.

Hmmm does this make it part of AsyncValidation then?

njlr avatar Sep 29 '25 13:09 njlr

Hmmm does this make it part of AsyncValidation then?

Yeah probably.

TheAngryByrd avatar Sep 29 '25 13:09 TheAngryByrd