First class functions


Дарова котаны, настала пора очередной порции умных штучек. Хочу поведать вам о частичном применении функции, чо эт и как им пользоваться.

Для начала хочу познакомить вас с умным словом функции первого класса Вот выписка из вики: В информатике язык программирования имеет функции первого класса, если он рассматривает функции как объекты первого класса. В частности, это означает, что язык поддерживает передачу функций в качестве аргументов другим функциям, возврат их как результат других функций, присваивание их переменным или сохранение в структурах данных

Давайте сразу к делу, возьмем самый сахарный пример, который естественно вы никогда не встретите в продакшене(а я вот встретил);

представьте простую функцию которая складывает 2 числа, назовем ее add:


const add = (a, b) => a + b;
add(0, 1) // 1;
add(10, 1) // 11;

на этом ее область применения заканчивается, функция полностью справляется со своими задачами, но давайте вколим немного стероидов в этого малыша. Как мы читканули с вики, функция может возвращать не только результат вычисления но и функцию, перепишем add:


const add = a => b => a + b;

Функция add принимает 1 аргумент и возвращает функцию, которая принимает еще 1 аргумент и делает вычисление:


add(0)(1) // 1;
add(10)(1) // 11;

Редаксоюзеры уже узнали подобное написание? connect(mapStateToProps)(MyComponent)

Пока нихрена удобного, найдем применение:


const inc = add(1);
inc(2) // 3;
inc(5) // 6;
inc(inc(0)) // 2;

const dec = add(-1);
dec(1) // 0;
dec(dec(2)) // 0;

мы не стали создавать новые однотипные функции, просто использовали базовую. Преобразование add(a,b) в add(a)(b) называется каррирование; Каждый раз вызывать функцию add с двумя скобками удручающе, поэтому в некоторых вспомогательных библиотеках(таких как рамда) есть понятие достаточно количество аргументов: если функция получила не достаточно аргументов то она вернет функцию ожидающее остаточные:


add(1, 2) === add(1)(2);

Подобный подход удобен когда вы создаете композиции функций, ну или вот живой пример: у менять есть функция которая проверяет длину строки, и возвращает тру или фолс:


  const lessThan = (a, b) => a < b;
  const noLongerThan = n => s => lessThan(length(s), n);

и я передаю функцию валидации очень просто:


const validate = noLongerThan(100);
const validate2 = noLongerThan(1000);

вот у меня есть 2 функции которые ожидают на вход строку и возвращают тру или фолс =) Еще я перелопачивал один объект в другой руками, например:


const A = { a:1 , b: { c: 3 }  };
const B = { a: b.c };

Это была интеграция платежки, и получаемый объект от магазина был абсолютно не похож на тот который должен был улететь в платежную систему, писать каждый раз key: a.b.c.d || null было не очень то и прикольно, на помощь пришли функции первого класса:


// функция pathOr получает на вход дефолтное значение, массив пути, и сам объект
const getFromPayloadByPath = p => pathOr(null, p.split('.'), payloadObject);

const sendingObject = {
    a: getFromPayloadByPath('c.b'),
}

таких сосисок у меня было оч много, только представьте сколько инфы нужно отправить в платежную систему =) Не стоит боятся того, что функция может вернуть другую функцию, стоит один раз попробовать, и вы в это влюбитесь так же как и я, это поначалу кажется странным и запутанным, но стоит преодолеть свой страх и открывается чудный мир из бесконечных возможностей. Следующий высер обещаю написать про композицию функций справа налево и слева направо, где буду применять функции первого класса.