Изучая F # в эти дни, я заметил, что в некоторых библиотеках, таких как этот или который один Есть некоторые похожие функции, которые кажутся распространенными в F #, но не могут их реально расшифровать, что они делают, для чего они?
let ap x f =
match f, x with
| Ok f , Ok x -> Ok (f x)
| Error e , _ -> Error e
| _ , Error e -> Error e
let inline (<*>) f x = ap x f
let inline (<!>) f x = Result.map f x
let inline lift2 f a b = f <!> a <*> b
Даже объединение комментариев с ними не очень помогает в моем понимании:
/// Sequential application
/// If the wrapped function is a success and the given result is a success the function is applied on the value.
/// Otherwise the exisiting error messages are propagated.
let ap x f =
match f,x with
| Ok f , Ok x -> Ok (f x)
| Error e , _ -> Error e
| _ , Error e -> Error e
/// Sequential application
/// If the wrapped function is a success and the given result is a success the function is applied on the value.
/// Otherwise the exisiting error messages are propagated.
let inline (<*>) f x = ap x f
/// Infix map, lifts a function into a Result and applies it on the given result.
let inline (<!>) f x = Result.map f x
/// Promote a function to a monad/applicative, scanning the monadic/applicative arguments from left to right.
let inline lift2 f a b = f <!> a <*> b
Я даже не вижу пример того, как их можно использовать, не знаю также, почему inline
был использован.
Если бы кто-нибудь мог намекнуть о том, насколько полезны эти функции, я был бы очень признателен.
2 ответа
Их называют «аппликативными функторами» (иногда просто «аппликативными»). Их цель - объединить данные из нескольких Something<'T>
с помощью функции. По сути, «поднятие» функции типа 'Arg1 -> 'Arg2 -> ... -> 'Result
в функцию типа Something<'Arg1> -> Something<'Arg2> -> ... -> Something<'Result>
.
Например, учитывая стандартный тип результата:
type Result<'T, 'Err> = Ok of 'T | Error of 'Err
У вас может быть несколько значений Result, которые вы хотите объединить вместе. Например, скажем, у вас есть форма с вводами firstName, lastName и age. У вас также есть тип результата Person
:
type Person = { firstName: string; lastName: string; age: int }
// string -> string -> int -> Person
let makePerson firstName lastName age =
{ firstName = firstName; lastName = lastName; age = age }
Значения, поступающие из вашей фактической формы, могут иметь тип Result<string, InputError>
или Result<int, InputError>
, который может быть Error
, если, например,. пользователь не ввел значение.
type InputError =
| FieldMissing of fieldName: string
// Other error cases...
Вы хотите объединить их в Result<Person, InputError>
, то есть Ok
, если все входы - Ok
, или Error
, если любой вход - Error
. Используя аппликатив, вы можете сделать это так:
// Result<string, InputError> -> Result<string, InputError> -> Result<int, InputError> -> Result<Person, InputError>
let makePersonResult firstName lastName age =
makePerson <!> firstName <*> lastName <*> age
// Example uses:
makePersonResult (Ok "John") (Ok "Doe") (Ok 42)
// --> Ok { firstName = "John"; lastName = "Doe"; age = 42 }
makePersonResult (Error (FieldMissing "firstName")) (Ok "Doe") (Ok 42)
// --> Error (FieldMissing "firstName")
Аналогичная концепция может быть применена ко многим другим типам, кроме Result, поэтому ей было дано имя. Например, аппликатив в Async<'T>
может запустить все асинхронные аргументы параллельно, а когда они закончат, объединить свои результаты в Async<'Result>
. Другой пример, аппликатив для 'T list
будет эквивалентен List.map2
или List.map3
стандартной библиотеки, но может быть обобщен на любое количество списков аргументов.
Примечание: если вы ищите «аппликативный функтор», большинство результатов, которые вы найдете, будет в Хаскеле, где вместо этого оператор карты, обычно пишущий <!>
на F #, записывает <$>
.
F # Скотта Влашина для развлечения и выгоды (https://fsharpforfunandprofit.com) имеет серию < em> Сопоставьте и примените, о боже! (https://fsharpforfunandprofit.com / posts / elevated-world-7), которая должна пролить больше света на это. Относительно вашего конкретного вопроса:
<!>
- это операторmap
, который применяет функциюf
и параметрx
к элементам структуры данных, которые вы отображаете, или, другими словами, поднимает функцию в область структуры данных, в данном случае типResult
.<*>
- это операторap
(применить), который распаковывает функцию, заключенную в повышенное значение, в поднятую функцию.lift2
- это в основном операторmap
для двухпараметрической функции.
Пожалуйста, посмотрите на блог, это действительно помогает!
Похожие вопросы
Новые вопросы
f#
F # - это сжатый, выразительный и эффективный функциональный и объектно-ориентированный язык для .NET, который помогает вам писать простой код для решения сложных задач.