Web Front-end/함수형 프로그래밍(FP)

[JS/함수형] Currying

Currying 

- Currying은 여러 개의 인자를 가진 함수를 호출 할 경우, 파라미터의 수보다 적은 수의 파라미터를 인자로 받으면 누락된 파라미터를 인자로 받는 기법을 말한다. HAskell , Scala와 같이 전통적인 함수형 언어에는 기본내장이 되어있지만, JS에서는 커링이 내장되어 있지 않습니다. 

그림으로 설명하자면, 위와 같은 형태로 바꾸자는거다!

왜 필요한가요 ?

함수안에 함수를 쓰는건 "함수형 프로그래밍"에서는 아주 흔한 광경이고, 그렇게 쓰다보면,

함수(함수(함수(함수(함수.....)와 같은 무한액자식 구성이 될 수 있고, 이렇게 되면, 가독성과 에러잡는데 엄청난 문제가 생기게 된다. 그리고, 사용하기도 어려워진다. 

 

Code(구현)

  • Currying의 간단한 예제부터 보자.
  • Currying은 함수가 재귀적으로(Nested)한 형태를 띄고 있을 때, 그걸 풀어해치기 위해 나온 개념이므로,  함수를 리턴하게 디자인된 패턴이다.
  • 이때, 받은 인자가 2개이상이면, 그 즉시, 그 함수를 실행하고, 아니라면, 인자를 또 받는 함수를 리턴한다. 
  • 따라서, 아래와 같은 코드로 작성이 가능하게 된다.
const curry = f =>
	(a,..._) => _.length ? f(a,..._) 
    	:(..._) =>f(a,..._);
    
const mult = curry((a,b) => a*b);

const mult3 = mult(3);

console.log(mult3(10)) // 3*10 =30
console.log(mult3(5)) // 3*5 = 15

Currying을 적용시킨 함수들과 Go가 합쳐지면, 엄청난 시너지 효과를 내게 되는데, 짝수를 뽑아내는 로직을 GoCurrying의 개념을 이용해서 작성해보자!

Map,Reduce,Filter,Go,Curry의 로직은 아래와 같이 구현되어있다고 하자.

const curry = f => (a,..._) => _.length ? f(a,..._)
              :(..._)=> f(a,..._);
//map
const map = curry((f,iter) =>{
  let res=[];
  for (const a of iter){
    res.push(f(a));
  }
  return res;
});
//Filter
const filter = curry((f,iter) =>{
  let res=[];
  for (const a of iter){
    if (f(a)) res.push(a);
  }
  return res;
})
//reduce
const reduce = curry((f,acc,iter) =>{
  if(!iter){
    iter=acc[Symbol.iterator]();
    acc=iter.next().value;
  }
  for (const a of iter){
    acc=f(acc,a);  
  }
  return acc;
})
//go
const go = (...args) => reduce((a,f)=>f(a),args);
const arr=[1,2,3,4,5,6,7]

아래와 같이, 나열을 하면, 짝수를 뽑아내는 로직이 만들어진다.

go(
	arr,
    filter(n=>n%2===0),
    map(n=>n)
    console.log
)

만약, Currying이 적용되지 않은 Map,Reduce ,Filter을 이용했다면, 어떻게 될까?아래와 같이, 함수를 직접 Function 형태로 넘겨줘야할 것이다.

go(
	arr,
    num=>filter(n=>n%2===0,num),
    num=>map(n=>n,num),
);

왜 이런 차이가 발생할까?

Filter속성에, Currying이 적용이 되어, Reduce가 실행되는 과정(정확히는 (a,f) => f(a) 과정 )에서 Filter가 다음 함수의 호출을 기다리기 때문이다. 마찬가지로, map 또한 Currying이므로, 뒤에서 또 다른 함수호출을 기다리게 되고, 이와같이 계속 꼬리에 꼬리를 무는 형식으로 한가지의 데이터에 대해서, 로직을 적용시킬 수 있게 된다.

See the Pen JS_currying by Doge (@DogeIsFree) on CodePen.

Ref

위 글은 유인동님의 함수형 프로그래밍과 JavaScript ES6+를 보고 작성한 글입니다.

https://www.inflearn.com/course/functional-es6/dashboard

 

함수형 프로그래밍과 JavaScript ES6+ - 인프런 | 강의

ES6+와 함수형 프로그래밍을 배울 수 있는 강의입니다. 이 강좌에서는 ES6+의 이터러블/이터레이터/제너레이터 프로토콜을 상세히 다루고 응용합니다. 이터러블을 기반으로한 함수형 프로그래밍,

www.inflearn.com