Skip to main content

Реалізація 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

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.