map, filter, reduce를 중첩해서 쓰면 안쪽부터 읽어야 해서 흐름이 역방향이 된다. go와 pipe가 이 문제를 해결한다.
첫 번째 인자를 그 다음 함수에 순서대로 통과시킨다. reduce로 구현한다.
const go = (...args) => reduce((a, f) => f(a), args);
go(
add(0, 1),
a => a + 10,
a => a + 100,
log
); // 111
앞에서 중첩해서 썼던 코드를 읽는 순서대로 펼 수 있다.
// 중첩
reduce(add, map(p => p.price, filter(p => p.price < 20000, products)));
// go로 펼기
go(
products,
products => filter(p => p.price < 20000, products),
products => map(p => p.price, products),
prices => reduce(add, prices),
log
);
go와 같은 원리인데 함수 자체를 반환한다.
const pipe = (f, ...fs) => (...as) => go(f(...as), ...fs);
const f = pipe(
(a, b) => a + b,
a => a + 10,
a => a + 100
);
log(f(0, 1)); // 111
함수의 인자가 부족하면 함수를 반환하고, 충분하면 즉시 실행한다.
const curry = f =>
(a, ..._) => _.length ? f(a, ..._) : (..._) => f(a, ..._);
const mult = curry((a, b) => a * b);
log(mult(3)(2)); // 6
log(mult(3, 2)); // 6
map, filter, reduce에 curry를 적용하면 go 안에서 훨씬 간결하게 쓸 수 있다.
const map = curry((f, iter) => { ... });
const filter = curry((f, iter) => { ... });
const reduce = curry((f, acc, iter) => { ... });
go(
products,
filter(p => p.price < 20000),
map(p => p.price),
reduce(add),
log
);
인자를 하나만 넘기면 함수를 반환하기 때문에 go 안에서 자연스럽게 연결된다.