How to modify this CE to return Error as a single item vs. a list, in F#?

I asked another question about how to make an application async/result expression.

User @Brian_Berns came up with a very good answer: Is there an async validate lib for F#?

The only caveat is that the Error part of a Result needs to be in an array, which works well for new code but doesn't play nicely with existing functions.

Brian's answer is:

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()

and his working example:

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"]

I haven't spent much time looking into building a CE, but I'm just going into it right now.

My question is how to return the Result.Error as an item vs. a list with an item. In the example, that means changing:

let doSomethingElseAsync () = async { return Error ["hello"] }
let andAnotherThingAsync () = async { return Error ["world"] }

to

let doSomethingElseAsync () = async { return Error "hello" }
let andAnotherThingAsync () = async { return Error "world" }

Updated answer

To make that work, we need a Source member in the builder that will do the conversion for us:

type AsyncValidationBuilder() =
    member _.Bind(av, f) = AsyncValidation.bind f av
    member _.Return(a) = AsyncValidation.ok a
    member _.MergeSources(av1, av2) = AsyncValidation.zip av1 av2
    member _.Source(asyncResult) : AsyncValidation<_, _> =
        async {
            let! result = asyncResult
            return Result.mapError List.singleton result
        }

The test code then looks like this:

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