gpt4 book ai didi

asynchronous - 具有异步操作的面向铁路的编程

转载 作者:行者123 更新时间:2023-12-04 02:38:18 30 4
gpt4 key购买 nike

以前问过类似的问题,但不知何故我找不到出路,再次尝试使用另一个示例。

可在 https://ideone.com/zkQcIU 获得作为起点的代码(略有删减)。 .

(识别 Microsoft.FSharp.Core.Result 类型有问题,不知道为什么)

基本上所有操作都必须通过前一个函数将结果提供给下一个函数进行流水线处理。操作必须是异步的,如果发生异常,它们应该向调用者返回错误。

要求是向调用者提供结果或错误。所有函数都返回一个填充了 的元组成功 type Article故障 type Error具有描述性的对象 codemessage从服务器返回。

将欣赏一个围绕我的代码的工作示例,用于回答中的被调用者和调用者。

被叫代码

type Article = {
name: string
}

type Error = {
code: string
message: string
}

let create (article: Article) : Result<Article, Error> =
let request = WebRequest.Create("http://example.com") :?> HttpWebRequest
request.Method <- "GET"
try
use response = request.GetResponse() :?> HttpWebResponse
use reader = new StreamReader(response.GetResponseStream())
use memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(reader.ReadToEnd()))
Ok ((new DataContractJsonSerializer(typeof<Article>)).ReadObject(memoryStream) :?> Article)
with
| :? WebException as e ->
use reader = new StreamReader(e.Response.GetResponseStream())
use memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(reader.ReadToEnd()))
Error ((new DataContractJsonSerializer(typeof<Error>)).ReadObject(memoryStream) :?> Error)

其余的链式方法 - 相同的签名和相似的机构。你实际上可以重用 create 的主体为 update , upload , 和 publish能够测试和编译代码。
let update (article: Article) : Result<Article, Error>
// body (same as create, method <- PUT)

let upload (article: Article) : Result<Article, Error>
// body (same as create, method <- PUT)

let publish (article: Article) : Result<Article, Error>
// body (same as create, method < POST)

来电显示
let chain = create >> Result.bind update >> Result.bind upload >> Result.bind publish
match chain(schemaObject) with
| Ok article -> Debug.WriteLine(article.name)
| Error error -> Debug.WriteLine(error.code + ":" + error.message)

编辑

根据答案并将其与 Scott 的实现 ( /image/bIxpD.png ) 进行匹配,以帮助进行比较和更好地理解。
let bind2 (switchFunction : 'a -> Async<Result<'b, 'c>>) = 
fun (asyncTwoTrackInput : Async<Result<'a, 'c>>) -> async {
let! twoTrackInput = asyncTwoTrackInput
match twoTrackInput with
| Ok s -> return! switchFunction s
| Error err -> return Error err
}

编辑 2 基于F#的bind实现
let bind3 (binder : 'a -> Async<Result<'b, 'c>>) (asyncResult : Async<Result<'a, 'c>>) = async {
let! result = asyncResult
match result with
| Error e -> return Error e
| Ok x -> return! binder x
}

最佳答案

看看Suave source code ,特别是 WebPart.bind功能。在 Suave 中,WebPart 是一个函数,它接受上下文(“上下文”是当前请求和到目前为止的响应)并返回类型 Async<context option> 的结果。 .将这些链接在一起的语义是如果异步返回 None ,跳过下一步;如果它返回 Some value ,下一步用 value 调用作为输入。这与 Result 的语义几乎相同。类型,因此您几乎可以复制 Suave 代码并将其调整为 Result 而不是 Option。例如,这样的事情:

module AsyncResult

let bind (f : 'a -> Async<Result<'b, 'c>>) (a : Async<Result<'a, 'c>>) : Async<Result<'b, 'c>> = async {
let! r = a
match r with
| Ok value ->
let next : Async<Result<'b, 'c>> = f value
return! next
| Error err -> return (Error err)
}

let compose (f : 'a -> Async<Result<'b, 'e>>) (g : 'b -> Async<Result<'c, 'e>>) : 'a -> Async<Result<'c, 'e>> =
fun x -> bind g (f x)

let (>>=) a f = bind f a
let (>=>) f g = compose f g

现在您可以按如下方式编写链:
let chain = create >=> update >=> upload >=> publish
let result = chain(schemaObject) |> Async.RunSynchronously
match result with
| Ok article -> Debug.WriteLine(article.name)
| Error error -> Debug.WriteLine(error.code + ":" + error.message)

注意:我无法通过在 F# Interactive 中运行它来验证此代码,因为我没有您的创建/更新/等的任何示例。职能。原则上它应该可以工作——所有类型都像乐高积木一样组合在一起,这就是你如何判断 F# 代码可能是正确的——但如果我犯了一个编译器会发现的错字,我还没有了解它。让我知道这是否适合您。

更新:在评论中,您询问是否需要同时拥有 >>=>=>定义了操作符,并提到您在 chain 中没有看到它们被使用。代码。我定义了两者是因为它们用于不同的目的,就像 |>>>运营商服务于不同的目的。 >>=就像 |> : 它将一个值传递给一个函数。虽然 >=>就像 >> : 它需要两个函数并将它们组合起来。如果您要在非 AsyncResult 上下文中编写以下内容:
let chain = step1 >> step2 >> step3

然后转化为:
let asyncResultChain = step1AR >=> step2AR >=> step3AR

我在哪里使用“AR”后缀来指示返回 Async<Result<whatever>> 的那些函数的版本类型。另一方面,如果您以通过管道传递数据的方式编写它:
let result = input |> step1 |> step2 |> step3

那么这将转化为:
let asyncResult = input >>= step1AR >>= step2AR >>= step3AR

所以这就是为什么你需要 bindcompose函数,以及与它们对应的运算符:这样您就可以拥有 |> 的等效项或 >> AsyncResult 值的运算符。

顺便说一句,我选择的运营商“名称”( >>=>=> ),我没有随机选择。这些是标准运算符,用于对 Async、Result 或 AsyncResult 等值进行“绑定(bind)”和“组合”操作。因此,如果您要定义自己的操作符,请坚持使用“标准”操作符名称,这样其他阅读您代码的人就不会感到困惑。

更新 2 :以下是阅读这些类型签名的方法:
'a -> Async<Result<'b, 'c>>

这是一个接受类型 A 的函数,并返回 Async缠绕在 Result . Result将类型 B 作为其成功案例,并将类型 C 作为其失败案例。
Async<Result<'a, 'c>>

这是一个值,而不是一个函数。这是一个 Async缠绕在 Result其中类型 A 是成功案例,类型 C 是失败案例。

所以 bind函数有两个参数:
  • 从 A 到异步的函数(B 或 C))。
  • 一个异步的值(A 或 C))。

  • 它返回:
  • 一个异步的值(B 或 C)。

  • 查看这些类型签名,您已经开始了解 bind 的含义。功能会做。它将采用 A 或 C 的值,并“解开”它。如果它是 C,它将产生一个“B 或 C”值,即 C(并且不需要调用该函数)。如果是 A,那么为了将其转换为“B 或 C”值,它会调用 f函数(需要一个 A)。

    所有这些都发生在异步上下文中,这为类型增加了额外的复杂性。如果您查看 basic version of Result.bind ,可能更容易掌握所有这些内容。 ,不涉及异步:
    let bind (f : 'a -> Result<'b, 'c>) (a : Result<'a, 'c>) =
    match a with
    | Ok val -> f val
    | Error err -> Error err

    在此代码段中, val 的类型是 'a ,以及 err 的类型是 'c .

    最终更新 :聊天 session 中有一条评论我认为值得保留在答案中(因为人们几乎从不关注聊天链接)。 Developer11 问道,

    ... if I were to ask you what Result.bind in my example code maps to your approach, can we rewrite it as create >> AsyncResult.bind update? It worked though. Just wondering i liked the short form and as you said they have a standard meaning? (in haskell community?)



    我的回答是:

    Yes. If the >=> operator is properly written, then f >=> g will always be equivalent to f >> bind g. In fact, that's precisely the definition of the compose function, though that might not be immediately obvious to you because compose is written as fun x -> bind g (f x) rather than as f >> bind g. But those two ways of writing the compose function would be exactly equivalent. It would probably be very instructive for you to sit down with a piece of paper and draw out the function "shapes" (inputs & outputs) of both ways of writing compose.

    关于asynchronous - 具有异步操作的面向铁路的编程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49399474/

    30 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com