Функции, каррирование и частичное применение
Функции в F# — полноценные значения, и все они каррированы, что открывает частичное применение.
Каррирование — представление функции нескольких аргументов как цепочки функций одного аргумента;
add a bна деле — функция отa, возвращающая функцию отb.
Функции — это значения
В F# функцию можно положить в let, передать аргументом, вернуть из другой функции. Объявления значения и функции выглядят одинаково — через let.
let square x = x * x
let apply f v = f v // f — функция-аргумент
printfn "%d" (apply square 7)Вывод:
49
Каррирование по умолчанию
Объявление let add a b = a + b имеет тип int -> int -> int. Стрелки читаются справа налево: это функция от a, которая возвращает функцию от b, которая возвращает int. Поэтому вызов с одним аргументом легален.
let add a b = a + b
let add10 = add 10 // частичное применение
printfn "%d" (add10 5)
printfn "%d" (add10 100)Вывод:
15 110
add 10 не вычисляет сумму, а возвращает новую функцию add10, которая ждёт второй аргумент. Это и есть частичное применение.
Зачем частичное применение
Оно позволяет создавать специализированные функции из общих, не повторяя код. Очень удобно при передаче функций в map, filter и подобные.
let multiply factor x = factor * x
let double = multiply 2
let triple = multiply 3
let xs = [1; 2; 3]
printfn "%A" (List.map double xs)
printfn "%A" (List.map triple xs)Вывод:
[2; 4; 6] [3; 6; 9]
Анонимные функции (лямбды)
Безымянную функцию задают через fun.
let nums = [1; 2; 3; 4]
let evens = List.filter (fun n -> n % 2 = 0) nums
printfn "%A" evensВывод:
[2; 4]
Как работает под капотом
Каждая функция нескольких аргументов компилируется как замыкание: применение первого аргумента создаёт объект, захватывающий это значение и реализующий вызов оставшихся аргументов. Для производительности компилятор оптимизирует «полные» вызовы (со всеми аргументами сразу) в прямой вызов без промежуточных замыканий, так что каррирование почти ничего не стоит, когда вы передаёте все аргументы.
Частые ошибки
- Считать, что вызов с неполным числом аргументов — ошибка. Это частичное применение.
- Путать порядок аргументов: для частичного применения «настроечный» параметр ставят первым.
- Ждать вычисления при
add 10— получаете функцию, а не число.
Итоги
- Функции — значения: их хранят, передают и возвращают.
- В F# функции каррированы:
a -> b -> c— цепочка функций одного аргумента. - Частичное применение создаёт специализированные функции из общих.
- Лямбды объявляют через
fun x -> ....