To make our composition more readable and easier to name we are going to ceate a compose function we can use to avoid having to manually nest our transducer calls.
We‘ll also go over semantically naming your compose functions for extra readability.
Nested style:
import {doubleTheNumber, evenOnly} from "../utils";
const map = xf => reducer => {
return (accumulation, value) => {
return reducer(accumulation, xf(value));
};
};
const filter = predicate => reducer => {
return (accumulation, value) => {
if (predicate(value)) return reducer(accumulation, value);
return accumulation;
};
};
const isEvenFilter = filter(evenOnly);
const isNot2Filter = filter(val => val !== 2);
const doubleMap = map(doubleTheNumber);
const pushReducer = (accumulation, value) => {
accumulation.push(value);
return accumulation;
};
[1,2,3,4].reduce(isNot2Filter(isEvenFilter(doubleMap(pushReducer))), []);
Compose function:
import {filter, map, evenOnly, doubleTheNumber} from "../utils";
const doubleMap = map(doubleTheNumber);
const isEvenFilter = filter(evenOnly);
const isNot2Filter = filter(val => val !== 2);
const pushReducer = (accumulation, value) => {
accumulation.push(value);
return accumulation;
};
[1, 2, 3, 4].reduce(isNot2Filter(isEvenFilter(doubleMap(pushReducer))), []);
// compose(f,g)(x) === f(g(x));
//
// compose(isNot2Filter, isEvenFilter, doubleMap)(pushReducer) ===
// isNot2Filter(isEvenFilter(doubleMap(pushReducer)));
const compose = (...functions) =>
functions.reduce((accumulation, fn) =>
(...args) => accumulation(fn(...args)), x => x);
[1, 2, 3, 4].reduce(
compose(isNot2Filter, isEvenFilter, doubleMap)(pushReducer),
[],
); /*?*/