Is there an async validate lib for F#?

I'm using asyncResult a lot in my code but it'll exit at the first error:

asyncResult {

    let! a = allGood()
    let! b = thisReturnsError()
    let! c = neverExecuted()
}

but sometimes I want to execute ALL functions and sum up the errors:

validation {
    let! a = doSomething()
    and! b = doSomethingElse()
    and! c = andAnotherThing()
}

and then I either get an Ok, or a Error with a list of errors.

This is great, but I would like to be able to do both!

asyncValidation {
    let! a = doSomethingAsync()
    and! b = doSomethingElseAsync()
    and! c = andAnotherThingAsync()
}

Is there a lib doing this?


I just threw this together, but I think it works:

type Validation<'a, 'err> = Result<'a, List<'err>>

type AsyncValidation<'a, 'err> = Async<Validation<'a, 'err>>

module AsyncValidation =

    let bind (f : _ -> AsyncValidation<_, _>) (av : AsyncValidation<_, _>) : AsyncValidation<_, _> =
        async {
            match! av with
                | Ok a -> return! (f a)
                | Error errs -> return Error errs
        }

    let ok a : AsyncValidation<_, _> =
        async { return Ok a }

    let zip (av1 : AsyncValidation<_, _>) (av2 : AsyncValidation<_, _>) : AsyncValidation<_, _> =
        async {
            let! v1 = av1
            let! v2 = av2
            match v1, v2 with
                | Ok a1, Ok a2 -> return Ok (a1, a2)
                | Error errs, Ok _
                | Ok _, Error errs -> return Error errs
                | Error errs1, Error errs2 -> return Error (errs1 @ errs2)
        }

type AsyncValidationBuilder() =
    member _.Bind(av, f) = AsyncValidation.bind f av
    member _.Return(a) = AsyncValidation.ok a
    member _.MergeSources(av1, av2) = AsyncValidation.zip av1 av2

let asyncValidation = AsyncValidationBuilder()

Test code:

let doSomethingAsync () = async { return Ok 1 }
let doSomethingElseAsync () = async { return Error ["hello"] }
let andAnotherThingAsync () = async { return Error ["world"] }
let test =
    asyncValidation {
        let! a = doSomethingAsync ()
        and! b = doSomethingElseAsync ()
        and! c = andAnotherThingAsync ()
        return a + b + c
    } |> Async.RunSynchronously
printfn "%A" test   // Error ["hello"; "world"]

With F#+ you can combine applicatives by using Compose:

#r @"nuget: FSharpPlus"

open FSharpPlus
open FSharpPlus.Data

// Generic applicative CE. At the moment not included in the library.
type ApplicativeBuilder<'a>() =
    inherit MonadFxStrictBuilder<'a>()
    member inline _.BindReturn(x, f) = map f x
let applicative<'t> = ApplicativeBuilder<'t> ()


let allGoodAsync () : Async<Validation<string, int>> = async { printfn "first success"; return Success 1 }
let thisResturnsErrorAsync () : Async<Validation<string, int>> = async { return Failure "thisReturnsError" }
let neverExecutedAsync () : Async<Validation<string, int>> = async { printfn "actually executed"; return Success 3 }

let x: Async<Validation<string, _>> =
    applicative {
        let! (a: int) = allGoodAsync () |> Compose
        and! (b: int) = thisResturnsErrorAsync () |> Compose
        and! (c: int) = neverExecutedAsync () |> Compose
        return a + b + c
        }
    |> Compose.run
let y = Async.RunSynchronously x

results in

> 
first success
actually executed
val y: Validation<string,int> = Failure "thisReturnsError"