Они называются «аппликативными функторами» (иногда просто «аппликативными»). Их цель - объединить данные из нескольких 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
, но может быть обобщен на любое количество списков аргументов.
Примечание: если вы ищите «аппликативный функтор», большинство результатов, которые вы найдете, будет в Haskell, где вместо оператора оператора карты, обычно пишется <!>
в F #, <$>
.