Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Реалізація compose». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**`compose()`** - це функція-комбінатор, яка збирає кілька функцій в одну: `compose(f, g, h)(x)` еквівалентно `f(g(h(x)))` (виконує справа наліво). **Ключове:** протилежний до нього `pipe()` виконує функції зліва направо.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення`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`.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.