Реалізація compose
compose() - це функція-комбінатор, яка збирає кілька функцій в одну:
compose(f, g, h)(x) еквівалентно f(g(h(x))) (виконує справа наліво).
Мінімальна реалізація (ES6)
javascript
// Універсальна версія: останній обробник може прийняти багато аргументів
const compose = (...fns) => (...args) =>
fns.reduceRight(
(acc, fn) => [fn(...acc)],
args
)[0];
// Приклад
const trim = s => s.trim();
const toInt = s => parseInt(s, 10);
const inc = n => n + 1;
const parseAndInc = compose(inc, toInt, trim);
parseAndInc(' 41 '); // 42Варіант для unary-функцій (трохи швидше і простіше)
javascript
const composeUnary = (...fns) => x =>
fns.reduceRight((v, fn) => fn(v), x);Promise-aware (асинхронний compose)
Працює як із sync-, так і з async-функціями.
javascript
const composeAsync = (...fns) => (...args) =>
fns
.slice(0, -1)
.reduceRight(
(p, fn) => p.then(res => fn(res)),
Promise.resolve(fns[fns.length - 1](...args))
);
// Приклад
const fetchUser = async id => ({ id, name: 'Tim' });
const getName = u => u.name;
const shout = s => s.toUpperCase();
const getUserNameLoud = composeAsync(shout, getName, fetchUser);
getUserNameLoud(7).then(console.log); // 'TIM'Типобезпечна версія для TypeScript (практичні перевантаження)
(Покривають до 4 функцій; можна розширити за аналогією.)
javascript
type Unary<A, R> = (a: A) => R;
export function compose<A, R>(f1: Unary<A, R>): Unary<A, R>;
export function compose<A, B, R>(
f1: Unary<B, R>,
f2: Unary<A, B>
): Unary<A, R>;
export function compose<A, B, C, R>(
f1: Unary<C, R>,
f2: Unary<B, C>,
f3: Unary<A, B>
): Unary<A, R>;
export function compose<A, B, C, D, R>(
f1: Unary<D, R>,
f2: Unary<C, D>,
f3: Unary<B, C>,
f4: Unary<A, B>
): Unary<A, R>;
export function compose(...fns: Function[]) {
return (x: unknown) => fns.reduceRight((v, f) => f(v), x);
}Часто потрібен і pipe() (зліва направо)
javascript
const pipe = (...fns) => (...args) =>
fns.reduce((acc, fn) => [fn(...acc)], args)[0];
const parseAndInc2 = pipe(trim, toInt, inc);
parseAndInc2(' 41 '); // 42Корисні нотатки
- Якщо компонуєте методи об'єктів, не забудьте про
this: прив'язуйтеfn.bind(obj). - Намагайтеся, щоб функції були чистими і унарними - композиція буде простішою і передбачуванішою.
- Для продакшену зручно мати обидва варіанти:
compose(R→L) іpipe(L→R), плюсcomposeAsync/pipeAsync.
Коротка відповідь
Для співбесідиPremium
Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.