Изучая 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 был использован.

Если бы кто-нибудь мог намекнуть о том, насколько полезны эти функции, я был бы очень признателен.

f#
1
Kerry Perret 31 Май 2019 в 11:25

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 #, записывает <$>.

5
Tarmil 31 Май 2019 в 09:09

F # Скотта Влашина для развлечения и выгоды (https://fsharpforfunandprofit.com) имеет серию < em> Сопоставьте и примените, о боже! (https://fsharpforfunandprofit.com / posts / elevated-world-7), которая должна пролить больше света на это. Относительно вашего конкретного вопроса:

  • <!> - это оператор map, который применяет функцию f и параметр x к элементам структуры данных, которые вы отображаете, или, другими словами, поднимает функцию в область структуры данных, в данном случае тип Result.
  • <*> - это оператор ap (применить), который распаковывает функцию, заключенную в повышенное значение, в поднятую функцию.
  • lift2 - это в основном оператор map для двухпараметрической функции.

Пожалуйста, посмотрите на блог, это действительно помогает!

5
dumetrulo 31 Май 2019 в 09:02