return 在异步块中的位置,在 F# 中

return position in an async block, in F#

我有以下代码:

let private SendOk (payload: 'a) : WebPart =
    payload |> Json.serialize |> OK >=> Writers.setMimeType "application/json"


let getBTCBalance (addresses: string list) : Async<WebPart> =
    async {
        try
            let! reply = blockExplorer.GetMultiAddressAsync addresses |> Async.AwaitTask
            return reply.Addresses |> Seq.map (fun x -> x.FinalBalance.GetBtc()) |> SendOk
        with
        | :? ArgumentNullException as ex -> return BAD_REQUEST    ex.Message
        | ex                             -> return INTERNAL_ERROR ex.Message
    }

它使用的是 Suave 网络服务器,并且两个路径(正确和错误)return WebPart.

类型的对象

如果我尝试移动 return 语句来包装其他所有内容,我会得到以下代码:

let getBTCBalance (addresses: string list) : Async<WebPart> =
    async {
        return ( 
            try
                let! reply = blockExplorer.GetMultiAddressAsync addresses |> Async.AwaitTask
                reply.Addresses |> Seq.map (fun x -> x.FinalBalance.GetBtc()) |> SendOk
            with
            | :? ArgumentNullException as ex -> BAD_REQUEST    ex.Message
            | ex                             -> INTERNAL_ERROR ex.Message
        )
    }

try/with 块应该 return 一个 WebPart 对象,我认为用一个 return 包装它会更好。

编译器不同意:

[FS0750] This construct may only be used within computation expressions

我不明白为什么会这样。谁能给我解释一下?

那是因为您正在使用 let!

let! 在异步计算中创建一个断点,将代码分成两部分,第二部分作为延续传递(请参阅 this answer 了解其工作原理)。

你不能 return 整个事情作为一个单一的表达,因为它实际上不是一个单一的表达,它只是看起来像一个。实际上,您的函数在 let! 之后结束,一个完全独立的函数开始。

这是您的问题的简化版本:

let f = async {
  return (let! x = doSomethingAsync)
}

这是否让您更好地了解出了什么问题?