Javascript Currying

커링이란 무엇인가. 뭔가 고급 기법 같아 보이긴 하는데, 나에게 필요 없는 것 같기도 하고. 근데 어려워 보이니까 배우고 싶고. 그래서 포스팅 하게 되었다.ㅠㅠ



1. 개념

위키피디아 에 의하면, 커링(Currying) 은 인자를 여러개 받는 함수를 분리하여, 인자를 하나 받는 함수의 체인(Chain) 으로 만드는 방법이다. Haskell Curry 란 분이 정리하셨다고 함. 헤스켈에서 유래한것 같으니, 헤스켈의 커링 부터 알아보자.

서광열님의 포스트 에 의하면, 헤스켈에서 다음과 같은 transfer 함수가 있다고 하자. Account, Account, int 타입의 인자를 받고 IO() 를 리턴하는 함수다.

Account -> Account -> Int -> IO()


그런데, 헤스켈에선 함수가 일급 객체(First Class Object) 이고 -> 결합 방향이 오른쪽임을 고려하면을 생각하면, 위의 함수는 아래와 같이 이해할 수 있다.

// Account 를 인자로 받아 
// (Account -> (Int -> IO ()))) 를 리턴

(Account -> (Account -> (Int -> IO ()))) 


이런식으로 결국 분해하다보면 3개의 인자를 받는다고 생각했던 transfer 함수를 1개의 인자를 받는 3개의 함수로 분리할 수 있다. 이제 하스켈 공식 홈페이지 있는 커링 설명을 보자. (원문은 여기)

f :: a -> b -> c
// is the curried form of
g :: (a, b) -> c

// You can convert these two types in either directions with the Prelude functions curry and uncurry.
f = curry g
g = uncurry f


두 가지 다 모두 같은 표현식이지만, Curried Formpartial application 을 작성할 수 있도록 해주므로 더 편리하다고 한다. partial applicaiton 은 커링된 함수를 통해 함수 조립식으로 애플리케이션을 작성하는 방법인 것 같다. 하스켈에선 모든 함수가 curried 되어 있단다.

다시 말해서 모든 함수가 하나의 인자만 취하므로 partial application 을 작성하기 쉽고, 그것이 편리하다는 뜻이다. 하스켈 이야기는 그만하고, 자바스크립트로 넘어가자.


2. Javascript Currying Example

Namelessja 님의 블로그 에서 샘플을 가져왔다.

var sum = function(a, b) {
    return a+b;
}


sum 이라는 이 함수를 재활용 할 수 있도록, 커링을 활용해 보자. 인자인 a 를 고정시켜 sum15, sum6 과 같은 함수를 만들어 낼 수 있다. 그런데, 기본적으로 자바스크립트에는 curry 함수가 없으므로 새로 만들자. 이왕이면 모든 함수가 사용할 수 있게 프로토타입에 넣어주자.

Function.prototype.curry = function() {
    var slice = Array.prototype.slice;
    var args = slice.apply(arguments);
    var that = this;
    return function() {
        return that.apply(null, args.concat(slice.apply(arguments)));
    };
}


그러면 이제 sum 함수를 재활용 하는것이 가능하다.

var sum15 = sum.curry(15);
var sum7 = sum.curry(7);

sum15(3); // return 18
sum7(13); // return 20


그럼 우리가 만든 curry 란 함수는 도대체 어떻게 동작하는걸까? 분석해보자.

Function.prototype.curry = function() {
    var slice = Array.prototype.slice;
    var args = slice.apply(arguments);
    var that = this;
    return function() {
        return that.apply(null, args.concat(slice.apply(arguments)));
    };
}


var sum5 = sum.curry(5) 를 호출하는 시점에서, 위 함수의 args[5]다. 여기에 that 이라는 변수에 sum 컨텍스트가 가게된다. sum5(7) 을 호출하는 시점에는 args[5]sum5arguments[7] 이 붙어 that.apply([5, 7]) 이 된다.

그런데 사실 thatsum 컨텍스트이므로 sum.aplly([5, 7]) 이다.


3. Practical Example of Currying in Javascript

스택오버플로우 : Javascript curry - what are the practical applications? 에 있는 답변을 가져왔다.

Function.prototype.curry = function() {
    var slice = Array.prototype.slice;
    var args = slice.apply(arguments);
    var that = this;
    return function() {
        return that.apply(null, args.concat(slice.apply(arguments)));
    };
}

function converter(toUnit, factor, offset, input) {
    offset = offset || 0;
    return [((offset+input)*factor).toFixed(2), toUnit].join(" ");
}

var milesToKm = converter.curry('km',1.60936,undefined);
var poundsToKg = converter.curry('kg',0.45460,undefined);
var farenheitToCelsius = converter.curry('degrees C',0.5556, -32);

milesToKm(10);            // returns "16.09 km"
poundsToKg(2.5);          // returns "1.14 kg"
farenheitToCelsius(98);   // returns "36.67 degrees C"


References

  1. Javascript 커링
  2. 커링(Currying)
  3. Google 라스클라 코딩단 그룹스



comments powered by Disqus