Kilka dni temu opublikowałem projekt na GitHubie, którego główną ideą
jest komponowanie funkcji. Źródła projektu składają się z jednej funkcji,
która należy do zbioru funkcji typu Higher-order Function
.
module.exports = function pipe(...fnList) {
const iterator = fnList[Symbol.iterator]();
let result = null;
function handle(...args) {
const fn = iterator.next().value;
if (typeof fn === 'undefined') return result;
if (typeof fn !== 'function') throw new TypeError();
result = fn(...args);
return handle(result);
}
return handle;
};
Pipe
Istnieje umowna zasada, à propos kolejność “składanych” funkcji.
Biblioteka, którą napisałem, nazywa się pipe
, bo uruchamia
funkcje w kolejności ich definicji.
Podobnie jak ma to miejsce w terminalu:
ll | wc -l # pipe
- na początku zostanie uruchomione polecenie
ll
, - a następnie
wc -l
.
f(plusOne, multiplyBy10)(x) = multiplyBy10(plusOne(x));
Użycie takiej funkcji sprowadza się do uruchomienia:
const plusOne = (v) => v + 1;
const multiplyBy10 = (v) => v * 10;
const fn = pipe(
plusOne,
multiplyBy10
);
console.log(fn(2)); // 30
Compose
Natomiast, co jeśli chcemy uruchamiać funkcje w odwrotnej kolejności?
Do tego właśnie służy function compose
.
f(multiplyBy10, plusOne)(x) = plusOne(multiplyBy10(x));
Użycie funkcji compose
(gdyby taka istniała) dałoby odmienny wynik:
const plusOne = (v) => v + 1;
const multiplyBy10 = (v) => v * 10;
const fn = compose(
plusOne,
multiplyBy10
);
console.log(fn(2)); // 21
•
PS Dziękuję @miloszpp za podpowiedź w temacie compose
vs pipe
.
Bibliografia
- … o Higher-order Function autorstwa Piotra Nalepy
- … o Currying-u autorstwa Daniela Idaszaka
- Więcej na temat programowania funkcyjnego znajdziesz na blogu — codewithstyle.info
- Jeśli jesteście ciekawi jak wygląda temat HOF w książce “Eloquent JavaScript” to zapraszam: https://eloquentjavascript.net/05_higher_order.html
- Mój dobry kolega — Kyle Simson — w swojej książce YDKJS także opisał tematykę domknięć