안녕하세요. 이번 시간에는 함수형 프로그래밍 기법 중 하나인 커링과 partial application에 대해 알아보겠습니다.
Partial application
쉽게 설명하자면, 여러 개의 인자를 받는 함수가 있을 때 일부의 인자를 고정한 함수를 만드는 기법입니다. 코드로 보면 이해하기 쉽습니다.
var plus = function(a, b, c) {
return a + b + c;
};
위와 같은 평범한 함수에서, partial application을 구현하기 위해 함수의 prototype에 특별한 메소드를 추가합니다. 그래야 모든 함수에서 partial application을 사용할 수 있으니까요.
Function.prototype.partial = function() {
var args = [].slice.apply(arguments);
var self = this;
return function() {
return self.apply(null, args.concat([].slice.apply(arguments)));
};
};
코드를 보죠. args는 arguments를 복사한 겁니다. 그리고 함수를 return 하는데 args는 클로저의 변수로 저장됩니다. 그리고 이제 새로운 함수에 인자가 들어왔을 때 기존에 있던 args와 concat하는거죠.
var plusa = plus.partial(1);
plusa(2, 3); // 6
var plusb = plusa.partial(2);
plusb(4); // 7
var plusab = plus.partial(1, 3);
plusab(5); // 9
이렇게 일부 인자만 받은 새로운 함수를 만들고, 나중에 새로운 함수에 인자를 넣어 완성합니다.
bind 함수를 쓰면 더 깔끔합니다. 위의 partial 메소드를 prototype에 추가할 필요 없이
var plusa = plus.bind(null, 1);
plusa(2, 3); // 6
var plusb = plusa.bind(null, 2);
plusb(4); // 7
var plusab = plus.bind(null, 1, 3);
plusab(5); // 9
하면 됩니다! 그냥 bind를 씁시다.
Currying
currying도 partial application처럼 인자를 미리 고정할 수 있지만 하나씩만 고정한다는 것이 특징입니다. 또한, 모든 인자를 받을 때까지 계속 함수를 생성합니다. 매 번 인자를 1개씩 고정하는 연속적인 partial application으로 볼 수도 있습니다.
function multiplyThree(x) {
return function(y) {
return function(z) {
return x * y * z;
}
};
}
multiplyThree(4)(8)(2); // 64
위의 함수는 일종의 커링입니다. 인자를 하나씩 세 번 받아야 호출됩니다.
그런데 인자가 100개라면, 저렇게 일일이 함수를 만들 수는 없겠죠? curry를 구현해봅시다.
Function.prototype.curry = function(one) {
var origFunc = this;
var target = origFunc.length;
var args = [];
function next(nextOne) {
args = args.concat(nextOne);
if (args.length === target) {
return origFunc.apply(null, args);
} else {
return function(nextOne) { return next(nextOne) };
}
}
return next(one);
}
이제 인자를 네 개를 받는 multiplyFour 함수에 커링을 적용해봅시다.
function multiplyFour(w, x, y, z) {
return w * x * y * z;
}
multiplyFour.curry(2)(3)(4)(5); // 120
커링이 적용되었습니다.
차이점
둘의 차이점을 간단히 정리해보겠습니다. partial application은 기존 함수의 매개변수들 중 일부를 미리 넣어둔 새로운 함수를 만드는 것입니다. 만들어진 partial application 함수는 다음 번 호출 시에는 결과를 반환해야 합니다.
반면에 currying은 기존 함수의 매개변수를 하나씩 받는 방법입니다. 매개변수를 모두 받을 때까지 새로운 함수를 반환하고요.
인자가 두 개일 때는 커링이나 파샬 어플리케이션이나 별 차이가 없습니다. (한 개일 때는... ㅎㅎ). 그리고 인자가 세 개 이상인 경우는 Partial application이나 커링 중에 선택하면 됩니다.
다음 시간에는 재귀와 메모이제이션에 대해 알아보겠습니다.