Async10
Async10

Reputation: 23

How to early return from async expression

loadCustomer has a return type of Async<Result<Customer, string>> and I only want to execute loadData if calling loadCustomer doesn't return an error. How can I achieve this without using a third party asyncResult expression or writing my own?

let getCustomerNameAndAmount customerId dataId =
    async {
        let! customer = loadCustomer customerId
        let! data = loadData dataId
        return customer |> Result.bind (fun c -> Ok (getNameOfCustomer c, data.Amount))
    }

Upvotes: 0

Views: 134

Answers (1)

Mankarse
Mankarse

Reputation: 40603

The operation that you are missing is ('a -> Async<Result<'b, 'e>>) -> Result<'a, 'e> -> Async<Result<'b, 'e>>

This can be written as:

let asyncResultBind<'a, 'b, 'e> (f : 'a -> Async<Result<'b, 'e>>) (a : Result<'a, 'e>) : Async<Result<'b, 'e>> =
    match a with
    | Ok v -> f v
    | Error x -> async {return Error x}

Now you can write getCustomerNameAndAmount as:

let getCustomerNameAndAmount customerId dataId =
    async {
        let! customer = loadCustomer customerId
        return! asyncResultBind
            (fun c ->
                async {
                    let! data = loadData dataId
                    return Ok (getNameOfCustomer c, data.Amount)
                }
            )
            customer
    }

Of course, this could also be written inline, without the abstraction of asyncResultBind:

let getCustomerNameAndAmount customerId dataId =
    async {
        match! loadCustomer customerId with
        |Ok c ->
            let! data = loadData dataId
            return Ok (getNameOfCustomer c, data.Amount)
        |Error e ->
            return Error e
    }

Upvotes: 3

Related Questions