在管道中将值传递给函数的第二个参数的正确方法是什么?
例如
async {
let! response =
someData
|> JsonConvert.SerializeObject
|> fun x -> new StringContent(x)
|> httpClient.PostAsync "/DoSomething"
|> Async.AwaitTask
}
上面的代码PostAsync
中的具有两个参数,即要发布到的URL和要发布的内容。也尝试过后管和括号,但不能完全弄清楚该怎么做
有什么想法吗?
答案 0 :(得分:5)
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
函数,依此类推。
答案 1 :(得分:3)
在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
}
我认为这也不应该是管道也有很好的理论上的理由-通常,如果您具有要通过一系列操作进行转换的“主要实体”,则管道可以很好地工作。通常可以是某些通用类型,例如list<'a>
或非通用类型,例如Chart
。
在您的示例中,您从对象开始,将其转换为JSON,然后将其转换为StringContent
,然后转换为Task<'T>
等。换句话说,它不是在转换实体-只是在做一个很多无关的东西。在这种情况下,我宁愿使用更明确的编码样式,而不是管道。