본문으로 바로가기

[JS] 코드를 값으로 다루는 go, pipe, curry 함수 만들기

category WEB/JS 2019. 5. 3. 23:19

코드를 값으로 다룬다면 전체적인 코드의 표현력을 높일 수 있다. 이를 위해 주로 사용하는 함수들인 go, pipe curry 함수를 한 번 구현해 보자. 우선 go 함수이다. go 함수는 인자로 함수들을 받아 차례로 실행시켜 결과에 해당하는 값을 리턴하는 함수이다.

const go = (...args) => reduce((a, f) => f(a), args);

go(
  0, 
  a => a + 1, 
  a => a + 10, 
  a => a + 100, 
  console.log	// 111
);

예시를 먼저 보면, 처음에 0을 받아 다음 인자로 넘기고, 그다음에는 인자로 받은 값을 +1, 그다음은 +10, 그다음은 +100 해준다. 그리고 마지막에는 인자를 console.log에 넣어준다. 이러한 go 함수는 reduce로 쉽게 구현이 가능하다. 인자들을 스프레드 연산자를 사용해 이터러블 객체로 받아오고, reduce로 해당 요소들을 차례로 실행하는 것이다.

 

pipe 함수는 go와 비슷하지만 값을 리턴하는 것이 아닌, 함수를 리턴하는 함수이다. go는 인자로 받은 함수들을 모두 실행시켜 결과에 해당하는 값을 리턴하지만, pipe는 인자로 받은 함수들을 모두 합쳐 합성된 함수를 리턴한다.

const pipe = (f, ...fs) => (...as) => go(f(...as), ...fs);

const f = pipe(
  (a, b) => a + b,
  a => a + 10,
  a => a + 100
);

console.log(f(0, 1));	// 111

pipe 함수는 내부적으로 go 함수를 사용한다. 단, pipe 함수의 첫 번째 인자를 값이 아닌 함수로 받을 수 있도록 하기 위해, 첫 번째 함수인 f와 나머지 함수인 ...fs를 따로 받는다. 그렇게 리턴된 합성된 함수는 인자(...as)를 받게 되면 go 함수를 의 인자로 함수를 넣어 차례로 함수를 실행시킬 때, 첫 번째 받은 함수에 해당 인자(...as)를 넣어 먼저 실행하게 하고, 그다음에 나머지 함수들을 차례로 실행시키도록 한다.

 

curry 함수는 받아둔 함수를 원하는 시점에 평가하도록 하는 함수이다. 여러개의 인자가 필요한데 하나의 인자만 받았을 경우 다음 인자를 받을 수 있도록 기다리는 함수를 만드는 것이다. 개념에 대해서는 따로 정리해둔 글이 있다.

const curry = f => (a, ..._) => (_.length ? f(a, ..._) : (..._) => f(a, ..._));

const mult = curry((a, b) => a * b);
console.log(mult(3)(2));	// 6

const mult3 = mult(3);
console.log(mult3(3));	// 9
console.log(mult3(4));	// 12

curry 함수를 받아 함수를 리턴하는데, 리턴된 함수의 인자가 여러개일 경우(_의 length가 0 이상일 경우)에는 받아둔 함수를 즉시 실행하고, 인자가 여러 개가 아닐 경우에는 함수를 다시 리턴하는데, 리턴된 함수가 인자를 받았을 때 앞서 받아둔 인자와 이후에 받은 인자를 합쳐서 실행하게 된다.

 

curry 함수는 앞서 구현했던 map, filter, reduce 함수와도 함께 사용할 수 있는데, 이렇게 만들어진 currying map, currying filter, currying reduce 함수는 go 함수와 함께 사용할 때, 리턴된 인자를 다음 함수의 두 번째 인자로 다시 넘겨줘야 하는 번거로움을 없애준다. 아래와 같이 말이다.

// Before
go(
  products,
  products => filter(p => p.price < 2500, products),
  products => map(p => p.price, products),
  prices => reduce(add, prices),
  console.log
);

// After
go(
  products,
  filter(p => p.price < 2500),
  map(p => p.price),
  reduce(add),
  console.log
);

함수형 프로그래밍과 ES6+ / 유인동님