Frontend/JS

[JavaScript] Array.prototype.join 보다 다형성 높은 join 함수

턴태 2023. 5. 8. 07:17

이전 강의 내용

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+