Skip to content

Conversation

@njlr
Copy link
Contributor

@njlr njlr commented Mar 7, 2025

Proposed Changes

  • Add Async.parallelMap2
  • Add Async.parallelMap3
  • Add Async.parallelZip
  • Add parallelAsyncResult
  • Add parallelAsyncValidation
  • Add tests

The main motivation is to be able to run Async<Result<_, _>> and AsyncValidation<_> workflows concurrently:

parallelAsyncResult {
  let! a = foo
  and! b = bar

  return a, b
}
parallelAsyncValidation {
  let! a = foo
  and! b = bar

  return a, b
}

I appreciate this is a pretty big PR - can split it up if needed!

Types of changes

What types of changes does your code introduce to FsToolkit.ErrorHandling?
Put an x in the boxes that apply and remove ones that don't apply

  • Bugfix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)

Checklist

Put an x in the boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your code.

  • Build and tests pass locally
  • I have added tests that prove my fix is effective or that my feature works (if appropriate)
  • I have added necessary documentation (if appropriate)

Further comments

Deviating from what was discussed here, I did not use Async.StartChild or Async.StartImmediateAsTask, instead opting for an Async.Parallel based implementation. Why?

Async.StartChild

This has the issue of not failing fast in the case of an exception.

let zipWithStartChild (a1: Async<'left>) (a2: Async<'right>) : Async<'left * 'right> =
    async.Bind(
        Async.StartChild a1,
        fun c1 ->
            async.Bind(
                Async.StartChild a2,
                fun c2 ->
                    async.Bind(c1, (fun r1 -> async.Bind(c2, (fun r2 -> async.Return(r1, r2)))))
            )
    )

open System

async {
    let! _ =
        zipWithStartChild 
            (Async.Sleep(TimeSpan.FromHours 1)) 
            (async { return failwith "Kaboom" })

    ()
}
|> Async.RunSynchronously

This terminates in 1 hour (probably).

It's order sensitive, which I think is a bit of a foot-gun; this terminates immediately:

async {
    let! _ =
        zipWithStartChild 
            (async { return failwith "Kaboom" }) 
            (Async.Sleep(TimeSpan.FromHours 1))

    ()
}
|> Async.RunSynchronously

In contrast, this fails immediately regardless of ordering:

open System

async {
    let! _ =
        Async.Parallel [|
            Async.Sleep(TimeSpan.FromHours 1)
            async { return failwith "Kaboom" }
        |]

    ()
}
|> Async.RunSynchronously

Async.StartImmediateAsTask

Tasks are not available in JavaScript Fable environments, whereas Async.Parallel is well supported across Fable targets.

@njlr
Copy link
Contributor Author

njlr commented Mar 7, 2025

Copy link
Collaborator

@TheAngryByrd TheAngryByrd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fantastic work ❤️.

Would you mind adding a few pages to the gitbook/ showing off a few examples in ce.md, map2.md, and map3.md?

@njlr
Copy link
Contributor Author

njlr commented Mar 17, 2025

Fantastic work ❤️.

Would you mind adding a few pages to the gitbook/ showing off a few examples in ce.md, map2.md, and map3.md?

Done 👍

@njlr njlr requested a review from TheAngryByrd March 18, 2025 22:21
Copy link
Collaborator

@TheAngryByrd TheAngryByrd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

@TheAngryByrd TheAngryByrd merged commit a3a9149 into demystifyfp:master Mar 21, 2025
26 of 27 checks passed
TheAngryByrd added a commit that referenced this pull request Mar 21, 2025
- BREAKING: [Remove Ply and update to FSharp 6](#248) Credits @TheAngryByrd
- BREAKING: [Remove MergeSources (and!) from some implementations like Result](#261)  Credits @TheAngryByrd
- BREAKING: [Merge TaskResult into Core library](#285) Credits @TheAngryByrd
- This means FsToolkit.ErrorHandling.TaskResult is no longer a separate package and will not be updated. It is now part of the core library.
- BREAKING: [Rename retn to singleton](#287) Credits @1eyewonder
- BREAKING: [Rename returnError to error + documentation](#311) Credits @tw0po1nt
- [Use Microsoft.Bcl.AsyncInterfaces in netstandard2.0 (Allows IAsyncDisposable and IAsyncEnumerable)](#250) Credits @TheAngryByrd
- [Build against Net8](#251) Credits @TheAngryByrd
- [Fix Overload Resolution to Align to Computation Expression used](#252) Credits @TheAngryByrd
- [refactor!: Seq.sequenceResultM returns Array instead of seq](#255) Credits @bartelink
- [feat(Seq): sequenceResultA](#255) Credits @bartelink
- [Updated uses of Seq.append](#290) Credits @1eyewonder
- [Add Option.traverseAsync and Option.sequenceAsync](#298 (comment)) Credits @tw0po1nt
- [Add Require and Check helper methods](#295) Credits @PI-Gorbo
- [Add new AsyncOption APIs and document all its other functions; minor fixes to documentation for Option module](#307) Credits @tw0po1nt
- [F# 9 support and nullness](#308) Credits @TheAngryByrd
- [Update IcedTasks 0.11.7](0a4cc7b) Credits @TheAngryByrd
- [Add TaskValidation module](#313) Credits @tw0po1nt
- [feat(Seq.traverse/sequence*)!: Yield arrays](#310) Credits @bartelink
- [Add ParallelAsync CEs](#318) Credits @njlr
TheAngryByrd added a commit that referenced this pull request Mar 30, 2025
- BREAKING: [Remove Ply and update to FSharp 6](#248) Credits @TheAngryByrd
- BREAKING: [Remove MergeSources (and!) from some implementations like Result](#261)  Credits @TheAngryByrd
- BREAKING: [Merge TaskResult into Core library](#285) Credits @TheAngryByrd
- This means FsToolkit.ErrorHandling.TaskResult is no longer a separate package and will not be updated. It is now part of the core library.
- BREAKING: [Rename retn to singleton](#287) Credits @1eyewonder
- BREAKING: [Rename returnError to error + documentation](#311) Credits @tw0po1nt
- [Use Microsoft.Bcl.AsyncInterfaces in netstandard2.0 (Allows IAsyncDisposable and IAsyncEnumerable)](#250) Credits @TheAngryByrd
- [Build against Net8](#251) Credits @TheAngryByrd
- [Fix Overload Resolution to Align to Computation Expression used](#252) Credits @TheAngryByrd
- [refactor!: Seq.sequenceResultM returns Array instead of seq](#255) Credits @bartelink
- [feat(Seq): sequenceResultA](#255) Credits @bartelink
- [Updated uses of Seq.append](#290) Credits @1eyewonder
- [Add Option.traverseAsync and Option.sequenceAsync](#298 (comment)) Credits @tw0po1nt
- [Add Require and Check helper methods](#295) Credits @PI-Gorbo
- [Add new AsyncOption APIs and document all its other functions; minor fixes to documentation for Option module](#307) Credits @tw0po1nt
- [F# 9 support and nullness](#308) Credits @TheAngryByrd
- [Update IcedTasks 0.11.7](0a4cc7b) Credits @TheAngryByrd
- [Add TaskValidation module](#313) Credits @tw0po1nt
- [feat(Seq.traverse/sequence*)!: Yield arrays](#310) Credits @bartelink
- [Add ParallelAsync CEs](#318) Credits @njlr
- [Add Option.sequenceAsyncResult and Option.traverseAsyncResult](#321) Credits @JayWearsSocks
TheAngryByrd added a commit that referenced this pull request May 1, 2025
- BREAKING: [Remove Ply and update to FSharp 6](#248) Credits @TheAngryByrd
- BREAKING: [Remove MergeSources (and!) from some implementations like Result](#261)  Credits @TheAngryByrd
- BREAKING: [Merge TaskResult into Core library](#285) Credits @TheAngryByrd
- This means FsToolkit.ErrorHandling.TaskResult is no longer a separate package and will not be updated. It is now part of the core library.
- BREAKING: [Rename retn to singleton](#287) Credits @1eyewonder
- BREAKING: [Rename returnError to error + documentation](#311) Credits @tw0po1nt
- [Use Microsoft.Bcl.AsyncInterfaces in netstandard2.0 (Allows IAsyncDisposable and IAsyncEnumerable)](#250) Credits @TheAngryByrd
- [Build against Net8](#251) Credits @TheAngryByrd
- [Fix Overload Resolution to Align to Computation Expression used](#252) Credits @TheAngryByrd
- [refactor!: Seq.sequenceResultM returns Array instead of seq](#255) Credits @bartelink
- [feat(Seq): sequenceResultA](#255) Credits @bartelink
- [Updated uses of Seq.append](#290) Credits @1eyewonder
- [Add Option.traverseAsync and Option.sequenceAsync](#298 (comment)) Credits @tw0po1nt
- [Add Require and Check helper methods](#295) Credits @PI-Gorbo
- [Add new AsyncOption APIs and document all its other functions; minor fixes to documentation for Option module](#307) Credits @tw0po1nt
- [F# 9 support and nullness](#308) Credits @TheAngryByrd
- [Update IcedTasks 0.11.7](0a4cc7b) Credits @TheAngryByrd
- [Add TaskValidation module](#313) Credits @tw0po1nt
- [feat(Seq.traverse/sequence*)!: Yield arrays](#310) Credits @bartelink
- [Add ParallelAsync CEs](#318) Credits @njlr
- [Add Option.sequenceAsyncResult and Option.traverseAsyncResult](#321) Credits @JayWearsSocks
- [Add traversals/sequences for Task and TaskResult in the Option module](#325) Credits @tw0po1nt
TheAngryByrd added a commit that referenced this pull request May 29, 2025
- BREAKING: [Remove Ply and update to FSharp 6](#248) Credits @TheAngryByrd
- BREAKING: [Remove MergeSources (and!) from some implementations like Result](#261)  Credits @TheAngryByrd
- BREAKING: [Merge TaskResult into Core library](#285) Credits @TheAngryByrd
- This means FsToolkit.ErrorHandling.TaskResult is no longer a separate package and will not be updated. It is now part of the core library.
- BREAKING: [Rename retn to singleton](#287) Credits @1eyewonder
- BREAKING: [Rename returnError to error + documentation](#311) Credits @tw0po1nt
- [Use Microsoft.Bcl.AsyncInterfaces in netstandard2.0 (Allows IAsyncDisposable and IAsyncEnumerable)](#250) Credits @TheAngryByrd
- [Build against Net8](#251) Credits @TheAngryByrd
- [Fix Overload Resolution to Align to Computation Expression used](#252) Credits @TheAngryByrd
- [refactor!: Seq.sequenceResultM returns Array instead of seq](#255) Credits @bartelink
- [feat(Seq): sequenceResultA](#255) Credits @bartelink
- [Updated uses of Seq.append](#290) Credits @1eyewonder
- [Add Option.traverseAsync and Option.sequenceAsync](#298 (comment)) Credits @tw0po1nt
- [Add Require and Check helper methods](#295) Credits @PI-Gorbo
- [Add new AsyncOption APIs and document all its other functions; minor fixes to documentation for Option module](#307) Credits @tw0po1nt
- [F# 9 support and nullness](#308) Credits @TheAngryByrd
- [Update IcedTasks 0.11.7](0a4cc7b) Credits @TheAngryByrd
- [Add TaskValidation module](#313) Credits @tw0po1nt
- [feat(Seq.traverse/sequence*)!: Yield arrays](#310) Credits @bartelink
- [Add ParallelAsync CEs](#318) Credits @njlr
- [Add Option.sequenceAsyncResult and Option.traverseAsyncResult](#321) Credits @JayWearsSocks
- [Add traversals/sequences for Task and TaskResult in the Option module](#325) Credits @tw0po1nt
- [Add ok and error helper functions to TaskResultOption and AsyncResultOption modules](#327) Credits @tw0po1nt
- [Add CancellableTaskOption module and CE + tests and documentation](#328) Credits @tw0po1nt
TheAngryByrd added a commit that referenced this pull request Jun 2, 2025
- BREAKING: [Remove Ply and update to FSharp 6](#248) Credits @TheAngryByrd
- BREAKING: [Remove MergeSources (and!) from some implementations like Result](#261)  Credits @TheAngryByrd
- BREAKING: [Merge TaskResult into Core library](#285) Credits @TheAngryByrd
- This means FsToolkit.ErrorHandling.TaskResult is no longer a separate package and will not be updated. It is now part of the core library.
- BREAKING: [Rename retn to singleton](#287) Credits @1eyewonder
- BREAKING: [Rename returnError to error + documentation](#311) Credits @tw0po1nt
- [Use Microsoft.Bcl.AsyncInterfaces in netstandard2.0 (Allows IAsyncDisposable and IAsyncEnumerable)](#250) Credits @TheAngryByrd
- [Build against Net8](#251) Credits @TheAngryByrd
- [Fix Overload Resolution to Align to Computation Expression used](#252) Credits @TheAngryByrd
- [refactor!: Seq.sequenceResultM returns Array instead of seq](#255) Credits @bartelink
- [feat(Seq): sequenceResultA](#255) Credits @bartelink
- [Updated uses of Seq.append](#290) Credits @1eyewonder
- [Add Option.traverseAsync and Option.sequenceAsync](#298 (comment)) Credits @tw0po1nt
- [Add Require and Check helper methods](#295) Credits @PI-Gorbo
- [Add new AsyncOption APIs and document all its other functions; minor fixes to documentation for Option module](#307) Credits @tw0po1nt
- [F# 9 support and nullness](#308) Credits @TheAngryByrd
- [Update IcedTasks 0.11.7](0a4cc7b) Credits @TheAngryByrd
- [Add TaskValidation module](#313) Credits @tw0po1nt
- [feat(Seq.traverse/sequence*)!: Yield arrays](#310) Credits @bartelink
- [Add ParallelAsync CEs](#318) Credits @njlr
- [Add Option.sequenceAsyncResult and Option.traverseAsyncResult](#321) Credits @JayWearsSocks
- [Add traversals/sequences for Task and TaskResult in the Option module](#325) Credits @tw0po1nt
- [Add ok and error helper functions to TaskResultOption and AsyncResultOption modules](#327) Credits @tw0po1nt
- [Add CancellableTaskOption module and CE + tests and documentation](#328) Credits @tw0po1nt
- [Remove paket, Enforce nullness on net9.0, remove mocha](#331) Credits @TheAngryByrd
- [Add TaskValueOption module, operators, and CE + tests and documentation](#329) Credits @tw0po1nt
TheAngryByrd added a commit that referenced this pull request Jun 2, 2025
- BREAKING: [Remove Ply and update to FSharp 6](#248) Credits @TheAngryByrd
- BREAKING: [Remove MergeSources (and!) from some implementations like Result](#261)  Credits @TheAngryByrd
- BREAKING: [Merge TaskResult into Core library](#285) Credits @TheAngryByrd
- This means FsToolkit.ErrorHandling.TaskResult is no longer a separate package and will not be updated. It is now part of the core library.
- BREAKING: [Rename retn to singleton](#287) Credits @1eyewonder
- BREAKING: [Rename returnError to error + documentation](#311) Credits @tw0po1nt
- [Use Microsoft.Bcl.AsyncInterfaces in netstandard2.0 (Allows IAsyncDisposable and IAsyncEnumerable)](#250) Credits @TheAngryByrd
- [Build against Net8](#251) Credits @TheAngryByrd
- [Fix Overload Resolution to Align to Computation Expression used](#252) Credits @TheAngryByrd
- [refactor!: Seq.sequenceResultM returns Array instead of seq](#255) Credits @bartelink
- [feat(Seq): sequenceResultA](#255) Credits @bartelink
- [Updated uses of Seq.append](#290) Credits @1eyewonder
- [Add Option.traverseAsync and Option.sequenceAsync](#298 (comment)) Credits @tw0po1nt
- [Add Require and Check helper methods](#295) Credits @PI-Gorbo
- [Add new AsyncOption APIs and document all its other functions; minor fixes to documentation for Option module](#307) Credits @tw0po1nt
- [F# 9 support and nullness](#308) Credits @TheAngryByrd
- [Update IcedTasks 0.11.7](0a4cc7b) Credits @TheAngryByrd
- [Add TaskValidation module](#313) Credits @tw0po1nt
- [feat(Seq.traverse/sequence*)!: Yield arrays](#310) Credits @bartelink
- [Add ParallelAsync CEs](#318) Credits @njlr
- [Add Option.sequenceAsyncResult and Option.traverseAsyncResult](#321) Credits @JayWearsSocks
- [Add traversals/sequences for Task and TaskResult in the Option module](#325) Credits @tw0po1nt
- [Add ok and error helper functions to TaskResultOption and AsyncResultOption modules](#327) Credits @tw0po1nt
- [Add CancellableTaskOption module and CE + tests and documentation](#328) Credits @tw0po1nt
- [Remove paket, Enforce nullness on net9.0, remove mocha](#331) Credits @TheAngryByrd
- [Add TaskValueOption module, operators, and CE + tests and documentation](#329) Credits @tw0po1nt
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants