Передать значение второму параметру в конвейере - PullRequest
1 голос
/ 13 апреля 2019

Как правильно передать значение второму параметру функции в конвейере?

Например,

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

в приведенном выше коде PostAsync принимает 2 параметра,URL для публикации и содержание, которое вы хотите опубликовать.Пробовал заднюю трубу тоже и скобки, но не могу понять, как это сделать

Есть идеи?

Ответы [ 2 ]

5 голосов
/ 13 апреля 2019

PostAsync имеет параметры без карри, которые не могут быть переданы по одному, они должны быть переданы все сразу.Вот почему вы всегда должны каррировать свои параметры.

Но, увы, вы не можете контролировать определение PostAsync, потому что это метод библиотеки .NET, поэтому вы должны обернуть его так или иначе,Есть несколько вариантов:

Опция 1 : использовать лямбда-выражение:

|> 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 и т. Д.

3 голосов
/ 15 апреля 2019

В F # вам часто приходится работать с API .NET, которые не предназначены для работы в качестве функциональных конвейеров.В этих случаях вы можете делать различные трюки, чтобы поместить его в (часто очень уродливый) конвейер, или вы можете использовать тот факт, что 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> и т. д. - другими словами, это не трансформирует сущность - это просто делает много несвязанных вещей.В этих ситуациях я предпочитаю использовать более явный стиль кодирования, а не конвейер.

...