Haskell에서는 다음과 같은 방식으로 함수를 정의하는 것이 가능하다.
inc = (1+)
이것이 가능한 이유는 + 함수가 보통 다른 언어에서처럼 "두 개의 수 x, y를 받아 하나의 수 z를 반환하는 함수 f: (x,y) → z"가 아니라 "하나의 수 x를 받아 '하나의 수 y를 받아 하나의 수 z를 반환하는 함수'를 반환하는 함수 f: x → (y → z)"이기 때문이다. 함수를 이런 식으로 정의하는 방법을 커링(currying), 앞의 방식은 언커링(uncurrying)이라고 한다.
커링을 하면 대부분의 경우 람다 함수를 쓸 필요가 없어지기 때문 프로그램이 간결하고 읽기 편하게 된다. 예를 들어 다음 두 가지 경우를 비교해보자.
(define (add x y) (+ x y)) ; 언커링한 경우는 람다 함수 사용 (map (lambda (x) (add x 1)) '(1 2 3 4)) ; 커링한 경우 (map (add 1) '(1 2 3 4))
불행히도 스킴은 언커링을 하기 때문에 이런 이점을 누릴 수 없다. 여기서는 스킴에서 함수를 커링하는 방법을 알아보자.
이미 존재하는 함수를 커링해주는 함수를 만들어보자.
(define (curry f . x)
(lambda (y)
(apply f (append x (list y)))))
위의 curry 함수는 함수 f와 매개변수 목록 x를 받아서 람다 함수를 반환한다. 람다 함수는 나머지 매개변수 목록 y를 받아서 x와 y를 합쳐서 완전한 매개변수 목록을 만든 다음에 f를 적용한다. 결과적으로 함수를 커링한 것처럼 사용할 수 있게 된다. 이제 앞의 예는 다음과 같이 표현할 수 있다.
(map (curry + 1) '(1 2 3 4))
함수를 정의할 때부터 커링을 하려면 매크로를 사용해야 한다. 다음 매크로는 Currying macro에서 가져온 것이다.
(define-syntax curried
(syntax-rules ()
((curried () body ...) (lambda () body ...))
((curried (arg) body ...) (lambda (arg) body ...))
((curried (arg args ...) body ...)
(lambda (arg . rest)
(let ((next (curried (args ...) body ...)))
(if (null? rest)
next
(apply next rest)))))))
(define-syntax define-curried
(syntax-rules ()
((define-curried (name args ...) body ...)
(define name (curried (args ...) body ...)))))
이제 함수를 정의할 때 define 대신 define-curried를 사용하면 된다.
(define-curried (add x y) (+ x y)) (map (add 1) '(1 2 3 4))