将值传递给管道中的第二个参数
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>
等等 - 换句话说,它不是转换一个实体——它只是在做很多不相关的事情。在那些情况下,我更喜欢使用更明确的编码风格而不是管道。
将值传递给管道中函数的第二个参数的正确方法是什么?
例如
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>
等等 - 换句话说,它不是转换一个实体——它只是在做很多不相关的事情。在那些情况下,我更喜欢使用更明确的编码风格而不是管道。