[JavaScript] Array.prototype.join 보다 다형성 높은 join 함수
이전 강의 내용
const queryStr = pipe(
Object.entries,
map(([k, v]) => `${k}=${v}`),
reduce((a, b) => `${a}&${b}`)
);
reduce를 보면 Array에 있는 join 함수와 같은 결과물을 반환한다. 그런데 Array의 join 메서드는 Array prototype에만 붙어있는 메서드다.
이때, reduce는 이터러블 객체를 순회하면서 값을 join하기에 조금 더 다형성이 높은 함수라고 볼 수 있다.
Join 함수
const join = curry((sep = ',', iter) =>
reduce((a, b) => `${a}${sep}${b}`, iter));
const queryStr = pipe(
Object.entries,
map(([k, v]) => `${k}=${v}`),
join('&')
);
console.log(queryStr({ limit: 10, offset: 10, type: 'notice' }));
join 함수는 separator와 iterable한 값을 받아서 reduce를 통해 iterable한 값을 순회하면서 중간에 separator를 넣는 함수다.
커스텀으로 생성한 join은 Array가 아니더라도 사용할 수 있어 다형성이 높다.
함수형 프로그래밍을 하면 파이프 사이사이에 있는 함수들을 조합하여 재사용성이 높게 활용할 수 있다.
예를 들어, well formed iterator를 반환하는 제너레이터를 순회한다고 했을 때,
function *a() {
yield 10;
yield 11;
yield 12;
yield 13;
}
console.log(join(' - ', a()));
// 10 - 11 - 12 - 13
아래와 같이 사용할 수 있어서 다형성이 높기에 자연스레 조합성 또한 높아진다. 이때, 이터러블 프로토콜을 따르고 있기 때문에 지연 평가를 할 수 있다는 것을 알 수 있다. reduce에서 next()를 통해 값을 하나씩 순회하기 때문이다.
이제 지연평가를 적용하여 함수 식을 바꿔보자
L.entries = function *(obj) {
for (const k in obj) yield [k, obj[k]];
};
const join = curry((sep = ',', iter) =>
reduce((a, b) => `${a}${sep}${b}`, iter));
const queryStr = pipe(
L.entries,
L.map(([k, v]) => `${k}=${v}`),
join('&')
);
console.log(queryStr({ limit: 10, offset: 10, type: 'notice' }));
지연 평가를 하고 있는지, L.entries와 L.map 사이에 console.log를 넣어보면 아래와 같은 값을 출력한다.
L.entries {<suspended>}
정상적으로 평가를 미루고 있음을 확인할 수 있다.
다형성이 높은 함수는 그 자체로 의미가 있는 함수이지만, 다형성이 높은 것을 활용해서 다양한 함수와 조합하여 사용할 수 있도록 함수를 조합 및 체이닝 할 수 있다는 점에서 굉장히 유용한 성질이라고 생각한다. 그리고, 이터러블 프로토콜을 따르는 것은 곧 지연 평가를 할 수 있다는 의미이며, 지연 평가를 할 수 있다는 것은 제때 원하는 때에만 평가를 실행할 수 있기 때문에 더 효율적으로 코드를 동작하게 할 수 있다는 것과 같다. 앞선 사례들을 적절히 혼합하여 성능 최적화를 해보면 좋을 것 같다.
출처: 인프런 함수형 프로그래밍과 JavaScript ES6+