힘들게 go를 이해하였다면 이제 pipe를 이해할 시간이다.
(슬슬 함수형 다워지고 있다.)
pipe는 reduce, go를 모두 사용한다.
const log = (t)=>console.log(t);
const redu = (f , acc ,iter) => {
if(!iter){
iter = acc[Symbol.iterator]();
acc = iter.next().value;
}
for(const cf of iter){
acc = f(cf, acc);
}
return acc;
};
// log(redu((a,b)=>a+b,[2,4,8]));
const go = (...args) => redu((cf,acc)=>cf(acc),args);
go(
0,
a=>a+1,
a=>a+11,
a=>a+111,
log
);
const pipe = (...fs) => (arg) => go(arg,...fs);
const pipeEx = pipe(
a=>a+1,
a=>a+19,
a=>a+33
);
log(pipeEx(0))
pipe는 우선 함수들만을 넘겨받는다.
그 부분이 바로 아래의 코드다
const pipe = (...fs) ...
const pipeEx = pipe(
a=>a+1,
fn1,
fn2,
...
)
pipe 함수는 pipeEx에 의해 선언됨과 동시에 인자들을 넘겨받고 바로 다음 일을 시작한다.
pipe 함수 입장에서 남은 일은 (arg)=>go(arg,...fs)
이다.
이 때 pipe는 ...fs
로 입력받은 함수들을 모두 기억하고 있는 상태이다.
pipe는 함수의 리턴 값으로 함수를 한 번 더 리턴한다. 그 함수에는 인자가 필요하다.
log(pipeEx(0))
<- 이 줄이 arg라는 변수를 받아 다시 pipe의 리턴 값의 함수에 값을 전달한다. pipe 함수의 리턴 함수의 리턴은 go 함수이므로 go는 두 인자를 받고 결국 go(0,[array]); 처럼 작동하게 된다.
이쯤 오면 그냥 객체를 정의하고 값을 적당히 계산한 다음 리턴해주고 싶어지는 욕구가 차오른다. 하지만 이런 함수형 프로그래밍의 장점이 눈에 보이지 않는 것도 아니다.
함수 내부에서 객체를 정의하지 않고 인자와 콜백함수 그리고 스코프 밖의 세상에 sideEffect를 일으키지 않으므로써 함수형 코드는 어디에 붙여도 환경을 깨트리는 일은 거의 없을 수 밖에 없다. 그 값을 이용해서 깨트린다는건 다른 차원의 문제이니까 말이다.