Przejdź do treści

compose vs pipe

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;
};

Listing 1. Cały kod projektu

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));

Listing 2. Matematyczny zapis składania funkcji.

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

Listing 3. “Składanie” funkcji w jedną.

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));

Listing 4. Matematyczny zapis składania funkcji.

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

Listing 5. “Składanie” funkcji w jedną.

PS Dziękuję @miloszpp za podpowiedź w temacie compose vs pipe.

Bibliografia