- Неизменность (Immutability)
- Идемпотентность
- Императивный и декларативный
- Карринг
- Частичное применение
- Запоминание
- Чистые функции
- Ссылочная прозрачность
- Композиция
Цель функционального программирования состоит в том, чтобы минимизировать побочные эффекты и разделить функции так, чтобы в случае ошибки ты точно знал, где её найти.
Неизменность (Immutability)
Неизменность заключается не в изменении состояния, а в копировании старого состояния, изменении новой копии и замене старого состояния новым.
Неизменяемые объекты проще создавать, тестировать и использовать. Действительно, неизменяемые объекты всегда потокобезопасны. Они помогают избежать временной связи.
Временная связь - это связь, которая возникает, когда есть два или более члена класса, которые должны быть вызваны в определенном порядке. Использование неизменяемых объектов происходит без побочных эффектов.
Постоянная структура данных - это структура данных, которая всегда сохраняет свою предыдущую версию при её изменении. Такие структуры данных фактически неизменяемы, так как их операции не обновляют (заметно) структуру на месте, а вместо этого, всегда дают новую обновленную структуру.
Подробнее о mutable и immutable структуры данных.
Идемпотентность
Идемпотентность (Idempotent) - означает предсказуемость, т.е. при одном и том же вводном значении, функция всегда должна возвращать один и тот же результат.
Императивный и декларативный
Императивный код - это код, который сообщает компьютеру, что и как делать. Компьютеры хороши с императивными инструкциями. Например: «Пожалуйста, подойди к столу, правой рукой возьми воду, вернись ко мне и дай воды».
for (let i = 0; i < 1000; i++) {
console.log(i);
}
Декларативный код - это код, который сообщает компьютеру, что делать и что должно происходить; он не говорит машине, как именно это сделать. Люди хороши в декларативных инструкциях. Например: «Пожалуйста, дай мне эту воду».
В этом примере мы не указываем, что начальный индекс 0
и что после каждой итерации его нужно увеличивать.
[1, 2, 3].forEach(i => console.log(i));
Карринг
Карринг (curry) - преобразует функцию с несколькими аргументами в серию функций, каждая из которых принимает один аргумент.
function multiply(a, b, c) {
return a *b * c;
}
function multiply(a) {
return (b) => {
return (c) => {
return a * b * c;
}
}
}
console.log(multiply(1)(2)(3)); // 6
Подробнее о карринг.
Частичное применение
Частичное применение (partial application) похоже на карринг - это процесс создания функции с меньшим количеством параметров.
Также, частичное применение - это применение к функции некоторых аргументов и возврат новой функции, в ожидании остальных аргументов. Примененные аргументы хранятся в замыкании (closure) и остаются доступными для любых из возвращенных функций в будущем, которые частично применены.
Подробнее о частичном применении.
Запоминание
Запоминание (memoization) - особый вид кеширования. Он кеширует возвращаемое значение на основе своих параметров. Таким образом, если параметры совпадают, вместо поиска функции и повторного вычисления возвращаемого значения, выполняется поиск значения. Это может сэкономить ценные вычислительные циклы.
Чтобы не загрязнять глобальное пространство имен, используем замыкание:
function memoizedAdd80WithClosure() {
let cache = {};
return function(n) {
if (n in cache) {
return cache[n]
} else {
console.log('симуляция длительного вычисления');
cache[n] = n + 80;
return cache[n];
}
}
}
const memoizedAndEnclosedFunction = memoizedAdd80WithClosure();
memoizedAndEnclosedFunction(80)
симуляция длительного вычисления
160
memoizedAndEnclosedFunction(80)
160
memoizedAndEnclosedFunction(80)
160
Чистые функции
- чистые функции всегда должны возвращать один и тот же вывод при одинаковом вводе;
- чистые функции легко тестировать, легко составлять и избегать ошибок;
- нет побочных эффектов: чистые функции не могут ничего изменить вне их самих.
Функция с побочным эффектом, изменяет массив вне функции:
const arr = [1, 2, 3];
const mutateArray = (arr) => {
arr.pop();
}
mutateArray(arr);
console.log(arr) // [1, 2]
Та же функциональность, но с чистой функцией (без побочных эффектов, так как создает новый массив с concat()
и его же возвращает). Это не влияет ни на что, вне его сферы действия.
const arr = [1, 2, 3];
const removeLastItem = (arr) => {
const newArr = [].concat(arr);
newArr.pop();
return newArr;
}
removeLastItem(arr); // [1, 2]
console.log(arr) // [1, 2, 3]
Подробнее о чистых функциях.
Ссылочная прозрачность
Ссылочная прозрачность (referential transparency) - обычно определяется как факт, что выражение в программе может быть заменено его значением (или чем-либо, имеющим то же значение) без изменения результата программы. Это подразумевает, что методы всегда должны возвращать одно и то же значение для данного аргумента, не оказывая никакого другого влияния.
const add = (x, y) => x + y;
add(1, 2); // 3
При повторном вызове метода add
с теми же вводными, можно заменить его на значение 3
.
Эта концепция важна, так как означает, что когда у нас есть ссылочно-прозрачная функция, она легко поддается кэшированию.
Композиция
Композиция - создание сложной функциональности за счет объединения более простых функций.
const compose = (...fns) => x => fns.reduceRight((acc, fn) => fn(acc), x);
Подробнее о композиции.