1ilsang

Developer who will be a Legend

커링 이해하기 + compose 구현

2020-02-17 1ilsangJavaScript

cover

함수형 프로그래밍의 시작점인 커링을 공부해 정리해 보려고 한다.

커링이란 무엇인가?

일단 커링은 인도 커리랑 전혀 관련이 없다(Cover 이미지는 유우머로 넣었다!). 커링은 수학자 ‘하스켈 커리(Haskell Curry)‘로 부터 유래되었다.

커링이란 다중 인수를 갖는 함수를 단일 함수를 갖는 함수들의 함수열로 바꾸는 것을 뜻한다(위키 - 커링).

말이 조금 어려운데 쉽게 풀어쓰면

const sum = (a, b) => a + b;

sum(1, 2); // 3

위와 같은 ‘다중 인수’ 함수 코드를

const sum = a => b => a + b;

sum(1)(2); // 3

와 같은 ‘단일 함수 함수열’로 변경한다는 뜻이다.

왜 커링을 사용하는가?

커링을 쓰는 가장 큰 이유는 엄청난 재사용성에 있으며 이 외에도 인자를 제한없이 받을 수 있고, 함수의 실행을 원하는 지점까지 미룰 수 있다.

위의 예를 가공해서 보자.

const sum = a => b => a + b;
const addThree = sum(3); // sum 함수는 실행되지 않는다.

addThree(4); // 4;

이 코드를 잘 보면 sum 함수에서 sum(3)(4)를 해주는 것이 아닌 sum(3)만 해준다. 이를 통해 남은 함수인자 (4)를 기다리게 할 수 있다.

또한 3을 강제시킨 addThree 함수를 만들어 냈다. 3이 강제된 함수에 원하는 숫자를 넣게 되면 3 + x로 값이 떨이지게 된다.

이처럼 커링은 수식을 표현하기에 아주 적합하며 재사용성이 좋으며 함수의 실행을 미룰 수 있다는 점에서 ‘비동기’관리에 탁월하다.

어디서 사용할 수 있을까?

(아래의 내용은 Do it! 리엑트 프로그래밍 234p 코드를 참고한 것입니다.)

lodash 나 여러 함수형 프로그래밍들을 보면 함수들을 묶는 함수가 존재한다.

이 중에서 함수들을 순차적으로 묶어주는 compose 를 구현해 보려고 한다.

const compose = (...fnArr) => {
  return fnArr.reduce(
    (acc, cur) => {
      return (...args) => cur(acc(...args));
    },
    k => k
  );
};

const sum = a => b => a + b;
const multiply = a => b => a * b;
const minus = a => b => a - b;

// 5 - (4 * (3 + x)) = ?
const ret = compose(
  sum(3), // 3 + x
  multiply(4), // 4 * x2
  minus(5) // 5 - x3
);

ret(1); // -11
// 3 + 1 = 4
// 4 * 4 = 16
// 5 - 16 = -11

const ret2 = minus(5)(multiply(4)(sum(3)(1)));

reduce 부분만 이해하면 기존의 커링 코드와 똑같은 것을 알 수 있다.

compose 함수에서 펼침연산으로 인자를 가져온다. 이렇게 하게 되면 Array.prototype.slice.call(arguments) 를 해주지 않아도 된다.

배열로 가져온 인자들을 reduce 로 돌리면서 cur(acc(...args)) 로 이전의 함수의 결과값이 현재 함수의 인자로 되도록해 순차적으로 함수들이 조립되게 한다.

compose 로 묶어준 함수들은 위에서부터 순차적으로 내려가는 반면, ret2 와 같이 커링 조합은 뒤에서부터(sum(3)(1)) 조합이 시작된다.

가독성이 첫번째가 더 좋은 것을 알 수 있다.

이는 React 의 HoC 나 Reducer 함수 조합 등에서 사용할 수 있다.

그럼 이만!