将值传递给管道中的第二个参数

Pass value to second parameter in a pipeline

将值传递给管道中函数的第二个参数的正确方法是什么?

例如

async {
    let! response =
        someData
        |> JsonConvert.SerializeObject
        |> fun x -> new StringContent(x)
        |> httpClient.PostAsync "/DoSomething"
        |> Async.AwaitTask
}

在上面的代码中PostAsync需要2个参数,url到post和你想要的内容post。也试过后管和括号,但不太清楚该怎么做

有什么想法吗?

PostAsync有非柯里化参数,不能一个一个传入,必须一次性全部传入。这就是为什么您应该始终对参数进行柯里化。

但是,唉,您无法控制 PostAsync 的定义,因为它是一个 .NET 库方法,所以您必须以某种方式包装它。有几个选项:

选项 1:使用 lambda 表达式:

|> fun body -> httpClient.PostAsync( "/DoSomething", body )

选项 2: 用柯里化参数声明自己的函数

let postAsync client url body = 
    client.PostAsync(url, body)

...

|> postAsync httpClient "/DoSomething"

这通常是我的首选选项:在使用 .NET API 之前,我总是将它们封装在 F# 格式中。这样更好,因为同一个包装器不仅可以转换参数,还可以转换其他内容,例如错误处理,或者在您的情况下,异步模型:

let postAsync client url body = 
    client.PostAsync(url, body) 
    |> Async.AwaitTask

选项 3:超级通用,让自己成为一个函数,用于将任何函数从非柯里化转换为柯里化。在其他函数式语言中,此类函数通常称为 uncurry:

let uncurry f a b = f (a, b)

...

|> uncurry httpClient.PostAsync "/DoSomething"

一个问题是它只适用于两个参数。如果你有一个带三个参数的非柯里化函数,你必须为它创建一个单独的 uncurry3 函数,依此类推。

在 F# 中,您经常需要使用 .NET API,这些 API 并非设计为作为功能管道正常工作。在这些情况下,您可以采取各种技巧使其适应(通常非常丑陋的)管道,或者您可以利用 F# 是多范式这一事实并以更面向对象的风格编写代码。

在你的例子中,我只会使用更多类似 C# 的风格,因为代码对管道不是很友好:

async {
    let serialized = JsonConvert.SerializeObject(someData)
    let postData = new StringContent(serialized)
    let! response = httpClient.PostAsync("/DoSomething", postData) |> Async.AwaitTask
}

我认为这不应该是管道也有一个很好的理论原因 - 通常,如果您有一些要通过一系列操作进行转换的 "main entity",管道会很好地工作。这通常可以是一些通用类型,例如 list<'a> 或非通用类型,例如 Chart.

在您的示例中,您从对象开始,将其转换为 JSON,然后将其转换为 StringContent,然后是 Task<'T> 等等 - 换句话说,它不是转换一个实体——它只是在做很多不相关的事情。在那些情况下,我更喜欢使用更明确的编码风格而不是管道。